From 11727ce32572391c492c50b1d18915d44b31aa68 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 14:24:37 +0000 Subject: [PATCH 001/121] docs: scaffold v5.0 overhaul (IA, redirects, generators, workflows) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lay the foundation for the RomM 5.0 documentation overhaul per the approved plan in /root/.claude/plans/we-dapper-piglet.md. Infrastructure: - pyproject.toml: add mkdocs-redirects, neoteroi-mkdocs, tomli - mkdocs.yml: wire mkdocs-redirects with full old-URL → new-URL map for every page in the existing nav; fix social-plugin .pngg typo - deploy.yml: only move the `latest` mike alias on `5.*` versions - deploy-v5-preview.yml: push v5 branch → mike alias `next` - pr-checks.yml: strict build + redirect-target verification + lychee link-check on changed Markdown - romm-release-bump.yml: nightly auto-PR pinning rommapp/romm to the latest release and regenerating snippets New IA (lowercase-hyphenated, with redirects from old Pascal-case slugs): - getting-started/, install/, administration/, administration/oidc/, using/, platforms/, ecosystem/, developers/, reference/, troubleshooting/, releases/, about/ - Navigation.md rewritten end-to-end - 106 placeholder pages tagged with `wave: 1|2|3` frontmatter Source-of-truth automation under docs/scripts/: - sources.toml pins the upstream rommapp/romm ref - gen_env_vars.py: parses env.template into a sectioned table (currently emits 94 vars from master) - gen_scheduled_tasks.py: emits the 7 scheduled + 3 manual + 1 watcher task table - gen_platforms.py: stub awaiting a 5.0 SHA pin - check_redirects.py: verifies every redirect target exists post-build (CI gate) - scaffold_ia.py: one-shot IA scaffolder (used to generate the placeholder pages) Wired three Wave-1 reference pages to include their generated snippets via pymdownx.snippets so the integration is provable end-to-end: - reference/environment-variables.md - reference/scheduled-tasks.md - platforms/supported-platforms.md (placeholder snippet for now) Verified `mkdocs build --strict` produces zero non-trivial warnings locally (only git-revision-date warnings on uncommitted files, which resolve on commit). --- .github/workflows/deploy-v5-preview.yml | 62 ++ .github/workflows/deploy.yml | 20 +- .github/workflows/pr-checks.yml | 71 ++ .github/workflows/romm-release-bump.yml | 85 ++ docs/Navigation.md | 169 +++- docs/about/brand-guidelines.md | 10 + docs/about/credits.md | 10 + docs/about/faqs.md | 10 + docs/about/license.md | 10 + docs/administration/administration-page.md | 10 + docs/administration/authentication.md | 10 + docs/administration/firmware-management.md | 10 + docs/administration/index.md | 10 + .../invitations-and-registration.md | 10 + docs/administration/metadata-providers.md | 10 + docs/administration/observability.md | 10 + docs/administration/oidc/authelia.md | 10 + docs/administration/oidc/authentik.md | 10 + docs/administration/oidc/index.md | 10 + docs/administration/oidc/keycloak.md | 10 + docs/administration/oidc/pocketid.md | 10 + docs/administration/oidc/zitadel.md | 10 + docs/administration/scanning-and-watcher.md | 10 + docs/administration/scheduled-tasks.md | 10 + docs/administration/server-stats.md | 10 + docs/administration/ssh-sync.md | 10 + docs/administration/users-and-roles.md | 10 + docs/developers/api-authentication.md | 10 + docs/developers/api-reference.md | 10 + docs/developers/architecture.md | 10 + docs/developers/contributing.md | 10 + docs/developers/development-setup.md | 10 + docs/developers/i18n.md | 10 + docs/developers/index.md | 10 + docs/developers/openapi.md | 10 + docs/developers/releasing.md | 10 + docs/developers/websockets.md | 10 + docs/ecosystem/argosy.md | 10 + docs/ecosystem/client-api-tokens.md | 10 + docs/ecosystem/community-apps.md | 10 + docs/ecosystem/device-sync-protocol.md | 10 + docs/ecosystem/fpkgi.md | 10 + docs/ecosystem/grout.md | 10 + docs/ecosystem/igir.md | 10 + docs/ecosystem/index.md | 10 + docs/ecosystem/kekatsu.md | 10 + docs/ecosystem/muos-app.md | 10 + docs/ecosystem/pkgj.md | 10 + docs/ecosystem/playnite-plugin.md | 10 + docs/ecosystem/tinfoil.md | 10 + docs/ecosystem/webrcade.md | 10 + docs/getting-started/concepts.md | 10 + docs/getting-started/first-scan.md | 10 + docs/getting-started/folder-structure.md | 10 + docs/getting-started/quick-start.md | 10 + docs/getting-started/what-is-new-in-5.md | 10 + docs/install/backup-and-restore.md | 10 + docs/install/databases.md | 10 + docs/install/docker-compose.md | 10 + docs/install/image-variants.md | 10 + docs/install/index.md | 10 + docs/install/kubernetes.md | 10 + docs/install/redis-or-valkey.md | 10 + docs/install/reverse-proxy.md | 10 + docs/install/synology.md | 10 + docs/install/truenas.md | 10 + docs/install/unraid.md | 10 + docs/platforms/custom-platforms.md | 10 + docs/platforms/emulatorjs-config.md | 10 + docs/platforms/firmware-by-platform.md | 10 + docs/platforms/index.md | 10 + docs/platforms/ms-dos.md | 10 + docs/platforms/ruffle-config.md | 10 + docs/platforms/supported-platforms.md | 13 + docs/reference/configuration-file.md | 10 + docs/reference/environment-variables.md | 16 + docs/reference/exports.md | 10 + docs/reference/feeds.md | 10 + docs/reference/glossary.md | 10 + docs/reference/ports-and-endpoints.md | 10 + docs/reference/scheduled-tasks.md | 12 + docs/releases/changelog.md | 10 + docs/releases/index.md | 10 + docs/releases/upgrading-to-3.0.md | 10 + docs/releases/upgrading-to-5.0.md | 10 + docs/resources/snippets/env-vars.md | 100 ++ docs/resources/snippets/scheduled-tasks.md | 15 + .../resources/snippets/supported-platforms.md | 6 + .../__pycache__/_sources.cpython-311.pyc | Bin 0 -> 2714 bytes docs/scripts/_sources.py | 40 + docs/scripts/check_redirects.py | 70 ++ docs/scripts/gen_env_vars.py | 97 ++ docs/scripts/gen_platforms.py | 37 + docs/scripts/gen_scheduled_tasks.py | 124 +++ docs/scripts/scaffold_ia.py | 167 ++++ docs/scripts/sources.toml | 10 + docs/troubleshooting/authentication.md | 10 + docs/troubleshooting/in-browser-play.md | 10 + docs/troubleshooting/index.md | 10 + docs/troubleshooting/kubernetes.md | 10 + docs/troubleshooting/miscellaneous.md | 10 + docs/troubleshooting/netplay.md | 10 + docs/troubleshooting/scanning.md | 10 + docs/troubleshooting/sync.md | 10 + docs/troubleshooting/synology.md | 10 + docs/using/account-and-profile.md | 10 + docs/using/collections.md | 10 + docs/using/console-mode.md | 10 + docs/using/downloads.md | 10 + docs/using/in-browser-play.md | 10 + docs/using/index.md | 10 + docs/using/languages.md | 10 + docs/using/library.md | 10 + docs/using/mobile-and-tv.md | 10 + docs/using/netplay.md | 10 + docs/using/pwa.md | 10 + docs/using/retroachievements.md | 10 + docs/using/rom-patcher.md | 10 + docs/using/saves-and-states.md | 10 + docs/using/smart-collections.md | 10 + docs/using/uploads.md | 10 + docs/using/virtual-collections.md | 10 + mkdocs.yml | 59 +- pyproject.toml | 4 +- uv.lock | 920 +++++++++++------- 125 files changed, 2723 insertions(+), 404 deletions(-) create mode 100644 .github/workflows/deploy-v5-preview.yml create mode 100644 .github/workflows/pr-checks.yml create mode 100644 .github/workflows/romm-release-bump.yml create mode 100644 docs/about/brand-guidelines.md create mode 100644 docs/about/credits.md create mode 100644 docs/about/faqs.md create mode 100644 docs/about/license.md create mode 100644 docs/administration/administration-page.md create mode 100644 docs/administration/authentication.md create mode 100644 docs/administration/firmware-management.md create mode 100644 docs/administration/index.md create mode 100644 docs/administration/invitations-and-registration.md create mode 100644 docs/administration/metadata-providers.md create mode 100644 docs/administration/observability.md create mode 100644 docs/administration/oidc/authelia.md create mode 100644 docs/administration/oidc/authentik.md create mode 100644 docs/administration/oidc/index.md create mode 100644 docs/administration/oidc/keycloak.md create mode 100644 docs/administration/oidc/pocketid.md create mode 100644 docs/administration/oidc/zitadel.md create mode 100644 docs/administration/scanning-and-watcher.md create mode 100644 docs/administration/scheduled-tasks.md create mode 100644 docs/administration/server-stats.md create mode 100644 docs/administration/ssh-sync.md create mode 100644 docs/administration/users-and-roles.md create mode 100644 docs/developers/api-authentication.md create mode 100644 docs/developers/api-reference.md create mode 100644 docs/developers/architecture.md create mode 100644 docs/developers/contributing.md create mode 100644 docs/developers/development-setup.md create mode 100644 docs/developers/i18n.md create mode 100644 docs/developers/index.md create mode 100644 docs/developers/openapi.md create mode 100644 docs/developers/releasing.md create mode 100644 docs/developers/websockets.md create mode 100644 docs/ecosystem/argosy.md create mode 100644 docs/ecosystem/client-api-tokens.md create mode 100644 docs/ecosystem/community-apps.md create mode 100644 docs/ecosystem/device-sync-protocol.md create mode 100644 docs/ecosystem/fpkgi.md create mode 100644 docs/ecosystem/grout.md create mode 100644 docs/ecosystem/igir.md create mode 100644 docs/ecosystem/index.md create mode 100644 docs/ecosystem/kekatsu.md create mode 100644 docs/ecosystem/muos-app.md create mode 100644 docs/ecosystem/pkgj.md create mode 100644 docs/ecosystem/playnite-plugin.md create mode 100644 docs/ecosystem/tinfoil.md create mode 100644 docs/ecosystem/webrcade.md create mode 100644 docs/getting-started/concepts.md create mode 100644 docs/getting-started/first-scan.md create mode 100644 docs/getting-started/folder-structure.md create mode 100644 docs/getting-started/quick-start.md create mode 100644 docs/getting-started/what-is-new-in-5.md create mode 100644 docs/install/backup-and-restore.md create mode 100644 docs/install/databases.md create mode 100644 docs/install/docker-compose.md create mode 100644 docs/install/image-variants.md create mode 100644 docs/install/index.md create mode 100644 docs/install/kubernetes.md create mode 100644 docs/install/redis-or-valkey.md create mode 100644 docs/install/reverse-proxy.md create mode 100644 docs/install/synology.md create mode 100644 docs/install/truenas.md create mode 100644 docs/install/unraid.md create mode 100644 docs/platforms/custom-platforms.md create mode 100644 docs/platforms/emulatorjs-config.md create mode 100644 docs/platforms/firmware-by-platform.md create mode 100644 docs/platforms/index.md create mode 100644 docs/platforms/ms-dos.md create mode 100644 docs/platforms/ruffle-config.md create mode 100644 docs/platforms/supported-platforms.md create mode 100644 docs/reference/configuration-file.md create mode 100644 docs/reference/environment-variables.md create mode 100644 docs/reference/exports.md create mode 100644 docs/reference/feeds.md create mode 100644 docs/reference/glossary.md create mode 100644 docs/reference/ports-and-endpoints.md create mode 100644 docs/reference/scheduled-tasks.md create mode 100644 docs/releases/changelog.md create mode 100644 docs/releases/index.md create mode 100644 docs/releases/upgrading-to-3.0.md create mode 100644 docs/releases/upgrading-to-5.0.md create mode 100644 docs/resources/snippets/env-vars.md create mode 100644 docs/resources/snippets/scheduled-tasks.md create mode 100644 docs/resources/snippets/supported-platforms.md create mode 100644 docs/scripts/__pycache__/_sources.cpython-311.pyc create mode 100644 docs/scripts/_sources.py create mode 100644 docs/scripts/check_redirects.py create mode 100644 docs/scripts/gen_env_vars.py create mode 100644 docs/scripts/gen_platforms.py create mode 100644 docs/scripts/gen_scheduled_tasks.py create mode 100644 docs/scripts/scaffold_ia.py create mode 100644 docs/scripts/sources.toml create mode 100644 docs/troubleshooting/authentication.md create mode 100644 docs/troubleshooting/in-browser-play.md create mode 100644 docs/troubleshooting/index.md create mode 100644 docs/troubleshooting/kubernetes.md create mode 100644 docs/troubleshooting/miscellaneous.md create mode 100644 docs/troubleshooting/netplay.md create mode 100644 docs/troubleshooting/scanning.md create mode 100644 docs/troubleshooting/sync.md create mode 100644 docs/troubleshooting/synology.md create mode 100644 docs/using/account-and-profile.md create mode 100644 docs/using/collections.md create mode 100644 docs/using/console-mode.md create mode 100644 docs/using/downloads.md create mode 100644 docs/using/in-browser-play.md create mode 100644 docs/using/index.md create mode 100644 docs/using/languages.md create mode 100644 docs/using/library.md create mode 100644 docs/using/mobile-and-tv.md create mode 100644 docs/using/netplay.md create mode 100644 docs/using/pwa.md create mode 100644 docs/using/retroachievements.md create mode 100644 docs/using/rom-patcher.md create mode 100644 docs/using/saves-and-states.md create mode 100644 docs/using/smart-collections.md create mode 100644 docs/using/uploads.md create mode 100644 docs/using/virtual-collections.md diff --git a/.github/workflows/deploy-v5-preview.yml b/.github/workflows/deploy-v5-preview.yml new file mode 100644 index 00000000..0a3dbe05 --- /dev/null +++ b/.github/workflows/deploy-v5-preview.yml @@ -0,0 +1,62 @@ +name: Deploy v5 Preview + +# Builds the v5 branch and publishes via mike under the `next` alias. +# Never touches `latest` — that alias only moves on the manual deploy.yml run. + +on: + push: + branches: [v5] + +concurrency: + group: pages + cancel-in-progress: true + +permissions: + contents: write + pages: write + id-token: write + +jobs: + deploy: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repo + uses: actions/checkout@v4.3.0 + with: + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v6.7.0 + with: + enable-cache: true + cache-dependency-glob: uv.lock + + - name: Set up Python + uses: actions/setup-python@v6.0.0 + with: + python-version-file: .python-version + + - name: Install dependencies + run: uv sync --all-extras --dev + + - name: Refresh generated snippets + run: | + uv run python docs/scripts/gen_env_vars.py + uv run python docs/scripts/gen_platforms.py + uv run python docs/scripts/gen_scheduled_tasks.py + + - name: Set Git user + run: | + git config --global user.name ${{ secrets.GIT_NAME }} + git config --global user.email ${{ secrets.GIT_EMAIL }} + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git + git fetch -a + git checkout gh-pages + git pull origin gh-pages + git checkout v5 + + - name: Deploy `next` alias via mike + run: uv run mike deploy --push --update-aliases 5.0-dev next + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e7e27bac..cce0ca72 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -28,6 +28,18 @@ jobs: with: fetch-depth: 0 + - name: Decide alias + id: alias + # Only `5.*` versions become `latest`. Older versions deploy as a + # frozen snapshot at // with no alias change. + run: | + version="${{ github.event.inputs.version }}" + if [[ "$version" =~ ^5(\.|$) ]]; then + echo "alias=latest" >> "$GITHUB_OUTPUT" + else + echo "alias=" >> "$GITHUB_OUTPUT" + fi + - name: Install uv uses: astral-sh/setup-uv@v6.7.0 with: @@ -42,6 +54,12 @@ jobs: - name: Install dependencies run: uv sync --all-extras --dev + - name: Refresh generated snippets + run: | + uv run python docs/scripts/gen_env_vars.py + uv run python docs/scripts/gen_platforms.py + uv run python docs/scripts/gen_scheduled_tasks.py + - name: Set Git user run: | git config --global user.name ${{ secrets.GIT_NAME }} @@ -54,6 +72,6 @@ jobs: git checkout main - name: Build the documentation - run: uv run mike deploy --push --update-aliases ${{ github.event.inputs.version }} latest + run: uv run mike deploy --push --update-aliases ${{ github.event.inputs.version }} ${{ steps.alias.outputs.alias }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 00000000..93fb9d89 --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,71 @@ +name: PR Checks + +on: + pull_request: + branches: [main, v5] + +concurrency: + group: pr-checks-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: read + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repo + uses: actions/checkout@v4.3.0 + + - name: Install uv + uses: astral-sh/setup-uv@v6.7.0 + with: + enable-cache: true + cache-dependency-glob: uv.lock + + - name: Set up Python + uses: actions/setup-python@v6.0.0 + with: + python-version-file: .python-version + + - name: Install dependencies + run: uv sync --all-extras --dev + + - name: Refresh generated snippets + run: | + uv run python docs/scripts/gen_env_vars.py + uv run python docs/scripts/gen_platforms.py + uv run python docs/scripts/gen_scheduled_tasks.py + + - name: Build docs (strict) + run: uv run mkdocs build --strict + + - name: Verify redirect targets exist + run: uv run python docs/scripts/check_redirects.py + + link-check: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout repo + uses: actions/checkout@v4.3.0 + with: + fetch-depth: 0 + + - name: Get changed Markdown files + id: changed + run: | + base="${{ github.event.pull_request.base.sha }}" + head="${{ github.event.pull_request.head.sha }}" + files=$(git diff --name-only "$base" "$head" -- 'docs/**/*.md' | tr '\n' ' ') + echo "files=$files" >> "$GITHUB_OUTPUT" + + - name: Run lychee on changed files + if: steps.changed.outputs.files != '' + uses: lycheeverse/lychee-action@v2 + with: + args: --no-progress --max-concurrency 4 ${{ steps.changed.outputs.files }} + fail: true diff --git a/.github/workflows/romm-release-bump.yml b/.github/workflows/romm-release-bump.yml new file mode 100644 index 00000000..6afaca04 --- /dev/null +++ b/.github/workflows/romm-release-bump.yml @@ -0,0 +1,85 @@ +name: RomM Release Bump + +# When rommapp/romm publishes a new release, update the pinned ref in +# docs/scripts/sources.toml, regenerate snippets, and open a PR. + +on: + schedule: + - cron: "0 6 * * *" # daily at 06:00 UTC + workflow_dispatch: + inputs: + ref: + description: "Override RomM ref (tag or SHA). Leave blank for latest release." + required: false + type: string + +permissions: + contents: write + pull-requests: write + +jobs: + bump: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repo + uses: actions/checkout@v4.3.0 + + - name: Resolve target ref + id: ref + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + override="${{ github.event.inputs.ref }}" + if [ -n "$override" ]; then + echo "ref=$override" >> "$GITHUB_OUTPUT" + else + latest=$(gh release view --repo rommapp/romm --json tagName -q .tagName) + echo "ref=$latest" >> "$GITHUB_OUTPUT" + fi + + - name: Update sources.toml + run: | + python - <<'PY' + import re, pathlib + ref = "${{ steps.ref.outputs.ref }}" + p = pathlib.Path("docs/scripts/sources.toml") + text = p.read_text() + new = re.sub(r'(?m)^ref\s*=.*$', f'ref = "{ref}"', text) + p.write_text(new) + PY + + - name: Install uv + uses: astral-sh/setup-uv@v6.7.0 + with: + enable-cache: true + cache-dependency-glob: uv.lock + + - name: Set up Python + uses: actions/setup-python@v6.0.0 + with: + python-version-file: .python-version + + - name: Install dependencies + run: uv sync --all-extras --dev + + - name: Regenerate snippets + run: | + uv run python docs/scripts/gen_env_vars.py + uv run python docs/scripts/gen_platforms.py + uv run python docs/scripts/gen_scheduled_tasks.py + + - name: Open PR + uses: peter-evans/create-pull-request@v7 + with: + commit-message: "Docs: sync with rommapp/romm@${{ steps.ref.outputs.ref }}" + branch: bot/romm-bump-${{ steps.ref.outputs.ref }} + title: "Docs: sync with rommapp/romm@${{ steps.ref.outputs.ref }}" + body: | + Auto-bump triggered by a new RomM release. + + - Pinned `docs/scripts/sources.toml` ref to `${{ steps.ref.outputs.ref }}`. + - Regenerated all source-of-truth snippets. + + Review the diff for any new env vars / endpoints / tasks that + need narrative documentation. diff --git a/docs/Navigation.md b/docs/Navigation.md index 2888b091..b3f85c3d 100644 --- a/docs/Navigation.md +++ b/docs/Navigation.md @@ -7,56 +7,123 @@ search: - [Introduction](index.md) - Getting Started - - [Quick Start Guide](Getting-Started/Quick-Start-Guide.md) - - [Folder Structure](Getting-Started/Folder-Structure.md) - - [Configuration File](Getting-Started/Configuration-File.md) - - [Metadata Providers](Getting-Started/Metadata-Providers.md) - - [Environment Variables](Getting-Started/Environment-Variables.md) - - [Reverse Proxy](Getting-Started/Reverse-Proxy.md) -- Platforms and Players - - [Supported Platforms](Platforms-and-Players/Supported-Platforms.md) - - Web Players - - [EmulatorJS](Platforms-and-Players/EmulatorJS-Player.md) - - [MS-DOS](Platforms-and-Players/MS-DOS.md) - - [RuffleRS](Platforms-and-Players/RuffleRS-Player.md) - - [Custom Platforms](Platforms-and-Players/Custom-Platforms.md) -- System Setup Guides - - [Unraid](System-Setup/Unraid-Setup-Guide.md) - - [With Docker Compose](System-Setup/Unraid-Compose-Setup.md) - - [Synology Setup](System-Setup/Synology-Setup-Guide.md) - - [TrueNAS Setup](System-Setup/TrueNAS-Setup-Guide.md) -- Usage - - [User Management](Usage/UserManagement.md) - - [Library Management](Usage/LibraryManagement.md) - - [Administration](Usage/Administration.md) -- OIDC Setup - - [OIDC Setup](Getting-Started/OIDC-Setup.md) - - [With Authelia](OIDC-Guides/OIDC-Setup-With-Authelia.md) - - [With Authentik](OIDC-Guides/OIDC-Setup-With-Authentik.md) - - [With PocketID](OIDC-Guides/OIDC-Setup-With-PocketID.md) - - [With Zitadel](OIDC-Guides/OIDC-Setup-With-Zitadel.md) + - [Quick Start](getting-started/quick-start.md) + - [Folder Structure](getting-started/folder-structure.md) + - [Your First Scan](getting-started/first-scan.md) + - [Core Concepts](getting-started/concepts.md) + - [What's New in 5.0](getting-started/what-is-new-in-5.md) +- Install & Deploy + - [Overview](install/index.md) + - [Docker Compose](install/docker-compose.md) + - [Image Variants](install/image-variants.md) + - [Databases](install/databases.md) + - [Redis or Valkey](install/redis-or-valkey.md) + - [Reverse Proxy](install/reverse-proxy.md) + - [Unraid](install/unraid.md) + - [Synology](install/synology.md) + - [TrueNAS](install/truenas.md) + - [Kubernetes](install/kubernetes.md) + - [Backup & Restore](install/backup-and-restore.md) +- Administration + - [Overview](administration/index.md) + - [Users & Roles](administration/users-and-roles.md) + - [Invitations & Registration](administration/invitations-and-registration.md) + - [Authentication](administration/authentication.md) + - OIDC Setup + - [Overview](administration/oidc/index.md) + - [Authelia](administration/oidc/authelia.md) + - [Authentik](administration/oidc/authentik.md) + - [Keycloak](administration/oidc/keycloak.md) + - [PocketID](administration/oidc/pocketid.md) + - [Zitadel](administration/oidc/zitadel.md) + - [Metadata Providers](administration/metadata-providers.md) + - [Scanning & Watcher](administration/scanning-and-watcher.md) + - [Scheduled Tasks](administration/scheduled-tasks.md) + - [Server Stats](administration/server-stats.md) + - [Observability](administration/observability.md) + - [Firmware Management](administration/firmware-management.md) + - [SSH Sync](administration/ssh-sync.md) + - [Administration Page](administration/administration-page.md) +- Using RomM + - [Overview](using/index.md) + - [Library](using/library.md) + - [Collections](using/collections.md) + - [Smart Collections](using/smart-collections.md) + - [Virtual Collections](using/virtual-collections.md) + - [Downloads](using/downloads.md) + - [Uploads](using/uploads.md) + - [In-Browser Play](using/in-browser-play.md) + - [Saves & States](using/saves-and-states.md) + - [RetroAchievements](using/retroachievements.md) + - [ROM Patcher](using/rom-patcher.md) + - [Netplay](using/netplay.md) + - [Console Mode](using/console-mode.md) + - [Install as PWA](using/pwa.md) + - [Mobile & TV](using/mobile-and-tv.md) + - [Account & Profile](using/account-and-profile.md) + - [Languages](using/languages.md) +- Platforms & Players + - [Overview](platforms/index.md) + - [Supported Platforms](platforms/supported-platforms.md) + - [Custom Platforms](platforms/custom-platforms.md) + - [MS-DOS](platforms/ms-dos.md) + - [EmulatorJS Configuration](platforms/emulatorjs-config.md) + - [Ruffle Configuration](platforms/ruffle-config.md) + - [Firmware by Platform](platforms/firmware-by-platform.md) +- Integrations & Ecosystem + - [Overview](ecosystem/index.md) + - First-party + - [Argosy Launcher](ecosystem/argosy.md) + - [Grout](ecosystem/grout.md) + - [Playnite Plugin](ecosystem/playnite-plugin.md) + - [muOS App](ecosystem/muos-app.md) + - Feeds + - [Tinfoil](ecosystem/tinfoil.md) + - [pkgj](ecosystem/pkgj.md) + - [fpkgi](ecosystem/fpkgi.md) + - [Kekatsu](ecosystem/kekatsu.md) + - [WebRcade](ecosystem/webrcade.md) + - For developers + - [Device Sync Protocol](ecosystem/device-sync-protocol.md) + - [Client API Tokens](ecosystem/client-api-tokens.md) + - [Community Apps](ecosystem/community-apps.md) + - [Igir Collection Manager](ecosystem/igir.md) - API & Development - - [Development Setup](API-and-Development/Development-Setup.md) - - [API Reference](API-and-Development/API-Reference.md) - - [Authentication](Getting-Started/Authentication.md) - - [Contributing](API-and-Development/Contributing.md) -- Tools - - [Igir Collection Manager](Tools/Igir-Collection-Manager.md) -- Maintenance - - [Scheduled Tasks](Maintenance/Scheduled-Tasks.md) - - [Migrating RomM](Maintenance/Migrating-to-new-machine.md) - - [Upgrading to 3.0](Maintenance/Upgrading-to-3.0.md) -- Integrations - - [Playnite plugin](Integrations/Playnite-plugin.md) - - [muOS app](Integrations/muOS-app.md) - - [Tinfoil Integration](Integrations/Tinfoil-integration.md) - - [pkgj Integration](Integrations/pkgj-integration.md) + - [Overview](developers/index.md) + - [API Reference](developers/api-reference.md) + - [API Authentication](developers/api-authentication.md) + - [WebSockets](developers/websockets.md) + - [Consuming OpenAPI](developers/openapi.md) + - [Development Setup](developers/development-setup.md) + - [Architecture](developers/architecture.md) + - [Contributing](developers/contributing.md) + - [Translations (i18n)](developers/i18n.md) + - [Releasing](developers/releasing.md) +- Reference + - [Environment Variables](reference/environment-variables.md) + - [Configuration File](reference/configuration-file.md) + - [Scheduled Tasks](reference/scheduled-tasks.md) + - [Exports](reference/exports.md) + - [Feeds](reference/feeds.md) + - [Ports & Endpoints](reference/ports-and-endpoints.md) + - [Glossary](reference/glossary.md) - Troubleshooting - - [Scanning Issues](Troubleshooting/Scanning-Issues.md) - - [Authentication Issues](Troubleshooting/Authentication-Issues.md) - - [Synology Issues](Troubleshooting/Synology-Issues.md) - - [Miscellaneous Issues](Troubleshooting/Miscellaneous-Troubleshooting.md) - - [Kubernetes Issues](Troubleshooting/Kubernetes-Issues.md) -- Miscellaneous - - [FAQs](Miscellaneous/FAQs.md) - - [Branding Guidelines](Miscellaneous/Brand-Guidelines.md) + - [Overview](troubleshooting/index.md) + - [Scanning](troubleshooting/scanning.md) + - [Authentication](troubleshooting/authentication.md) + - [In-Browser Play](troubleshooting/in-browser-play.md) + - [Netplay](troubleshooting/netplay.md) + - [Device Sync](troubleshooting/sync.md) + - [Synology](troubleshooting/synology.md) + - [Kubernetes](troubleshooting/kubernetes.md) + - [Miscellaneous](troubleshooting/miscellaneous.md) +- Release Notes & Migration + - [Overview](releases/index.md) + - [Upgrading to 5.0](releases/upgrading-to-5.0.md) + - [Upgrading to 3.0](releases/upgrading-to-3.0.md) + - [Changelog](releases/changelog.md) +- About + - [FAQs](about/faqs.md) + - [Brand Guidelines](about/brand-guidelines.md) + - [License](about/license.md) + - [Credits](about/credits.md) diff --git a/docs/about/brand-guidelines.md b/docs/about/brand-guidelines.md new file mode 100644 index 00000000..9a271824 --- /dev/null +++ b/docs/about/brand-guidelines.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Brand Guidelines + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/about/credits.md b/docs/about/credits.md new file mode 100644 index 00000000..b6d7bea4 --- /dev/null +++ b/docs/about/credits.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Credits + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/about/faqs.md b/docs/about/faqs.md new file mode 100644 index 00000000..fd1e139f --- /dev/null +++ b/docs/about/faqs.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# FAQs + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/about/license.md b/docs/about/license.md new file mode 100644 index 00000000..e2f79dec --- /dev/null +++ b/docs/about/license.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# License + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/administration-page.md b/docs/administration/administration-page.md new file mode 100644 index 00000000..786d00b8 --- /dev/null +++ b/docs/administration/administration-page.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Administration Page + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md new file mode 100644 index 00000000..c133a664 --- /dev/null +++ b/docs/administration/authentication.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Authentication + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md new file mode 100644 index 00000000..6769bd5e --- /dev/null +++ b/docs/administration/firmware-management.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Firmware Management + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/index.md b/docs/administration/index.md new file mode 100644 index 00000000..6535cfa0 --- /dev/null +++ b/docs/administration/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Administration + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md new file mode 100644 index 00000000..59299726 --- /dev/null +++ b/docs/administration/invitations-and-registration.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Invitations & Registration + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md new file mode 100644 index 00000000..745db946 --- /dev/null +++ b/docs/administration/metadata-providers.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Metadata Providers + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/observability.md b/docs/administration/observability.md new file mode 100644 index 00000000..6fe99d53 --- /dev/null +++ b/docs/administration/observability.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Observability + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/oidc/authelia.md b/docs/administration/oidc/authelia.md new file mode 100644 index 00000000..50c60085 --- /dev/null +++ b/docs/administration/oidc/authelia.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# OIDC with Authelia + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md new file mode 100644 index 00000000..4cbf0c3e --- /dev/null +++ b/docs/administration/oidc/authentik.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# OIDC with Authentik + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/oidc/index.md b/docs/administration/oidc/index.md new file mode 100644 index 00000000..ce3cafaa --- /dev/null +++ b/docs/administration/oidc/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# OIDC Setup + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/oidc/keycloak.md b/docs/administration/oidc/keycloak.md new file mode 100644 index 00000000..1e255e14 --- /dev/null +++ b/docs/administration/oidc/keycloak.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# OIDC with Keycloak + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/oidc/pocketid.md b/docs/administration/oidc/pocketid.md new file mode 100644 index 00000000..b3ffacb0 --- /dev/null +++ b/docs/administration/oidc/pocketid.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# OIDC with PocketID + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/oidc/zitadel.md b/docs/administration/oidc/zitadel.md new file mode 100644 index 00000000..3d69080e --- /dev/null +++ b/docs/administration/oidc/zitadel.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# OIDC with Zitadel + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md new file mode 100644 index 00000000..0d5b889b --- /dev/null +++ b/docs/administration/scanning-and-watcher.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Scanning & Watcher + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/scheduled-tasks.md b/docs/administration/scheduled-tasks.md new file mode 100644 index 00000000..9a133f76 --- /dev/null +++ b/docs/administration/scheduled-tasks.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Scheduled Tasks + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md new file mode 100644 index 00000000..4a499154 --- /dev/null +++ b/docs/administration/server-stats.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Server Stats + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/ssh-sync.md b/docs/administration/ssh-sync.md new file mode 100644 index 00000000..dd0492ba --- /dev/null +++ b/docs/administration/ssh-sync.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# SSH Sync + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md new file mode 100644 index 00000000..1f91d251 --- /dev/null +++ b/docs/administration/users-and-roles.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Users & Roles + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md new file mode 100644 index 00000000..6b219733 --- /dev/null +++ b/docs/developers/api-authentication.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# API Authentication + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md new file mode 100644 index 00000000..c6fb9d8e --- /dev/null +++ b/docs/developers/api-reference.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# API Reference + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md new file mode 100644 index 00000000..90904db3 --- /dev/null +++ b/docs/developers/architecture.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Architecture + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md new file mode 100644 index 00000000..64041efd --- /dev/null +++ b/docs/developers/contributing.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Contributing + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md new file mode 100644 index 00000000..77b1c466 --- /dev/null +++ b/docs/developers/development-setup.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Development Setup + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/i18n.md b/docs/developers/i18n.md new file mode 100644 index 00000000..6c999d63 --- /dev/null +++ b/docs/developers/i18n.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Translations (i18n) + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/index.md b/docs/developers/index.md new file mode 100644 index 00000000..6c836d9c --- /dev/null +++ b/docs/developers/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# API & Development + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/openapi.md b/docs/developers/openapi.md new file mode 100644 index 00000000..1a27157f --- /dev/null +++ b/docs/developers/openapi.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Consuming OpenAPI + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/releasing.md b/docs/developers/releasing.md new file mode 100644 index 00000000..7953f9fa --- /dev/null +++ b/docs/developers/releasing.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Releasing + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md new file mode 100644 index 00000000..49fe094a --- /dev/null +++ b/docs/developers/websockets.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# WebSockets + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/argosy.md b/docs/ecosystem/argosy.md new file mode 100644 index 00000000..c8fa5f93 --- /dev/null +++ b/docs/ecosystem/argosy.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Argosy Launcher + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md new file mode 100644 index 00000000..6cd34893 --- /dev/null +++ b/docs/ecosystem/client-api-tokens.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Client API Tokens + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/community-apps.md b/docs/ecosystem/community-apps.md new file mode 100644 index 00000000..9c7b29e3 --- /dev/null +++ b/docs/ecosystem/community-apps.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Community Apps + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md new file mode 100644 index 00000000..00720519 --- /dev/null +++ b/docs/ecosystem/device-sync-protocol.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Device Sync Protocol + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/fpkgi.md b/docs/ecosystem/fpkgi.md new file mode 100644 index 00000000..495eb231 --- /dev/null +++ b/docs/ecosystem/fpkgi.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# fpkgi + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/grout.md b/docs/ecosystem/grout.md new file mode 100644 index 00000000..dec13f32 --- /dev/null +++ b/docs/ecosystem/grout.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Grout + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md new file mode 100644 index 00000000..37737b22 --- /dev/null +++ b/docs/ecosystem/igir.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Igir Collection Manager + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md new file mode 100644 index 00000000..7a67eca1 --- /dev/null +++ b/docs/ecosystem/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Integrations & Ecosystem + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/kekatsu.md b/docs/ecosystem/kekatsu.md new file mode 100644 index 00000000..b5df388e --- /dev/null +++ b/docs/ecosystem/kekatsu.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Kekatsu + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md new file mode 100644 index 00000000..bfb2bfc8 --- /dev/null +++ b/docs/ecosystem/muos-app.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# muOS App + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/pkgj.md b/docs/ecosystem/pkgj.md new file mode 100644 index 00000000..e046870e --- /dev/null +++ b/docs/ecosystem/pkgj.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# pkgj + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/playnite-plugin.md b/docs/ecosystem/playnite-plugin.md new file mode 100644 index 00000000..7df2e3f4 --- /dev/null +++ b/docs/ecosystem/playnite-plugin.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Playnite Plugin + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/tinfoil.md b/docs/ecosystem/tinfoil.md new file mode 100644 index 00000000..2d1aa7d9 --- /dev/null +++ b/docs/ecosystem/tinfoil.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Tinfoil + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/ecosystem/webrcade.md b/docs/ecosystem/webrcade.md new file mode 100644 index 00000000..adf4e9d2 --- /dev/null +++ b/docs/ecosystem/webrcade.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# WebRcade + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/getting-started/concepts.md b/docs/getting-started/concepts.md new file mode 100644 index 00000000..efa1ec03 --- /dev/null +++ b/docs/getting-started/concepts.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Core Concepts + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md new file mode 100644 index 00000000..b5c7fff4 --- /dev/null +++ b/docs/getting-started/first-scan.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Your First Scan + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/getting-started/folder-structure.md b/docs/getting-started/folder-structure.md new file mode 100644 index 00000000..6f918ee6 --- /dev/null +++ b/docs/getting-started/folder-structure.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Folder Structure + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md new file mode 100644 index 00000000..80fe69ca --- /dev/null +++ b/docs/getting-started/quick-start.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Quick Start + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/getting-started/what-is-new-in-5.md b/docs/getting-started/what-is-new-in-5.md new file mode 100644 index 00000000..9958abd0 --- /dev/null +++ b/docs/getting-started/what-is-new-in-5.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# What's New in 5.0 + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/backup-and-restore.md b/docs/install/backup-and-restore.md new file mode 100644 index 00000000..88751be2 --- /dev/null +++ b/docs/install/backup-and-restore.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Backup & Restore + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/databases.md b/docs/install/databases.md new file mode 100644 index 00000000..ac41e0c2 --- /dev/null +++ b/docs/install/databases.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Databases + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md new file mode 100644 index 00000000..f5ae050c --- /dev/null +++ b/docs/install/docker-compose.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Docker Compose + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/image-variants.md b/docs/install/image-variants.md new file mode 100644 index 00000000..669d54d2 --- /dev/null +++ b/docs/install/image-variants.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Image Variants + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/index.md b/docs/install/index.md new file mode 100644 index 00000000..36226b4a --- /dev/null +++ b/docs/install/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Install & Deploy + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md new file mode 100644 index 00000000..6d9abd12 --- /dev/null +++ b/docs/install/kubernetes.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Kubernetes + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/redis-or-valkey.md b/docs/install/redis-or-valkey.md new file mode 100644 index 00000000..fcd9d02d --- /dev/null +++ b/docs/install/redis-or-valkey.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Redis or Valkey + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/reverse-proxy.md b/docs/install/reverse-proxy.md new file mode 100644 index 00000000..6551646b --- /dev/null +++ b/docs/install/reverse-proxy.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Reverse Proxy + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/synology.md b/docs/install/synology.md new file mode 100644 index 00000000..ca418eb9 --- /dev/null +++ b/docs/install/synology.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Synology + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/truenas.md b/docs/install/truenas.md new file mode 100644 index 00000000..69bd03a7 --- /dev/null +++ b/docs/install/truenas.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# TrueNAS + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/install/unraid.md b/docs/install/unraid.md new file mode 100644 index 00000000..aa20e124 --- /dev/null +++ b/docs/install/unraid.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Unraid + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md new file mode 100644 index 00000000..8ca08700 --- /dev/null +++ b/docs/platforms/custom-platforms.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Custom Platforms + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/platforms/emulatorjs-config.md b/docs/platforms/emulatorjs-config.md new file mode 100644 index 00000000..1994410f --- /dev/null +++ b/docs/platforms/emulatorjs-config.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# EmulatorJS Configuration + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/platforms/firmware-by-platform.md b/docs/platforms/firmware-by-platform.md new file mode 100644 index 00000000..439a9547 --- /dev/null +++ b/docs/platforms/firmware-by-platform.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Firmware by Platform + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/platforms/index.md b/docs/platforms/index.md new file mode 100644 index 00000000..1d57dea0 --- /dev/null +++ b/docs/platforms/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Platforms & Players + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md new file mode 100644 index 00000000..8c361ddb --- /dev/null +++ b/docs/platforms/ms-dos.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# MS-DOS + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/platforms/ruffle-config.md b/docs/platforms/ruffle-config.md new file mode 100644 index 00000000..f502191e --- /dev/null +++ b/docs/platforms/ruffle-config.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Ruffle Configuration + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/platforms/supported-platforms.md b/docs/platforms/supported-platforms.md new file mode 100644 index 00000000..41a03c16 --- /dev/null +++ b/docs/platforms/supported-platforms.md @@ -0,0 +1,13 @@ +--- +status: placeholder +wave: 2 +--- + +# Supported Platforms + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + The full table will be generated by `docs/scripts/gen_platforms.py` once a + 5.0 SHA is pinned in `docs/scripts/sources.toml`. The current `docs/Platforms-and-Players/Supported-Platforms.md` + remains the source for now. + +--8<-- "supported-platforms.md" diff --git a/docs/reference/configuration-file.md b/docs/reference/configuration-file.md new file mode 100644 index 00000000..1cfb8ff0 --- /dev/null +++ b/docs/reference/configuration-file.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Configuration File + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md new file mode 100644 index 00000000..9cb4d9ed --- /dev/null +++ b/docs/reference/environment-variables.md @@ -0,0 +1,16 @@ +--- +status: placeholder +wave: 1 +--- + +# Environment Variables + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + Narrative content for this page is still being written for Wave 1. The + table below is generated automatically from + [`rommapp/romm`'s `env.template`][src] at the SHA pinned in + `docs/scripts/sources.toml`. + + [src]: https://github.com/rommapp/romm/blob/master/env.template + +--8<-- "env-vars.md" diff --git a/docs/reference/exports.md b/docs/reference/exports.md new file mode 100644 index 00000000..c0a7f4ba --- /dev/null +++ b/docs/reference/exports.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Exports + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/reference/feeds.md b/docs/reference/feeds.md new file mode 100644 index 00000000..e977bc4f --- /dev/null +++ b/docs/reference/feeds.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Feeds + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md new file mode 100644 index 00000000..22ec3cce --- /dev/null +++ b/docs/reference/glossary.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Glossary + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md new file mode 100644 index 00000000..c727cd18 --- /dev/null +++ b/docs/reference/ports-and-endpoints.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 3 +--- + +# Ports & Endpoints + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 3) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/reference/scheduled-tasks.md b/docs/reference/scheduled-tasks.md new file mode 100644 index 00000000..f01ed106 --- /dev/null +++ b/docs/reference/scheduled-tasks.md @@ -0,0 +1,12 @@ +--- +status: placeholder +wave: 1 +--- + +# Scheduled Tasks + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + Narrative content for this page is still being written for Wave 1. The + table below is generated by `docs/scripts/gen_scheduled_tasks.py`. + +--8<-- "scheduled-tasks.md" diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md new file mode 100644 index 00000000..f4839d3a --- /dev/null +++ b/docs/releases/changelog.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Changelog + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/releases/index.md b/docs/releases/index.md new file mode 100644 index 00000000..1b494f98 --- /dev/null +++ b/docs/releases/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Release Notes & Migration + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/releases/upgrading-to-3.0.md b/docs/releases/upgrading-to-3.0.md new file mode 100644 index 00000000..fff6f8d3 --- /dev/null +++ b/docs/releases/upgrading-to-3.0.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Upgrading to 3.0 + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/releases/upgrading-to-5.0.md b/docs/releases/upgrading-to-5.0.md new file mode 100644 index 00000000..74584654 --- /dev/null +++ b/docs/releases/upgrading-to-5.0.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Upgrading to 5.0 + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/resources/snippets/env-vars.md b/docs/resources/snippets/env-vars.md new file mode 100644 index 00000000..46f2ec7c --- /dev/null +++ b/docs/resources/snippets/env-vars.md @@ -0,0 +1,100 @@ + + +### General + +| Variable | Default | Description | +| --- | --- | --- | +| `ROMM_BASE_PATH` | `/path/to/romm_mock` | — | +| `ROMM_TMP_PATH` | `_(unset)_` | — | +| `KIOSK_MODE` | `false` | — | +| `IGDB_CLIENT_ID` | `_(unset)_` | IGDB credentials | +| `IGDB_CLIENT_SECRET` | `_(unset)_` | — | +| `MOBYGAMES_API_KEY` | `_(unset)_` | Mobygames | +| `SCREENSCRAPER_USER` | `_(unset)_` | Screenscraper | +| `SCREENSCRAPER_PASSWORD` | `_(unset)_` | — | +| `STEAMGRIDDB_API_KEY` | `_(unset)_` | SteamGridDB | +| `RETROACHIEVEMENTS_API_KEY` | `_(unset)_` | RetroAchievements | +| `PLAYMATCH_API_ENABLED` | `_(unset)_` | Playmatch | +| `LAUNCHBOX_API_ENABLED` | `_(unset)_` | LaunchBox | +| `HASHEOUS_API_ENABLED` | `_(unset)_` | Hasheous | +| `FLASHPOINT_API_ENABLED` | `_(unset)_` | Flashpoint Project | +| `HLTB_API_ENABLED` | `_(unset)_` | HowLongToBeat | +| `TGDB_API_ENABLED` | `_(unset)_` | TheGamesDB | +| `ROMM_DB_DRIVER` | `mariadb` | Database config | +| `DB_HOST` | `127.0.0.1` | — | +| `DB_PORT` | `3306` | — | +| `DB_NAME` | `romm` | — | +| `DB_USER` | `romm` | — | +| `DB_PASSWD` | `_(unset)_` | — | +| `DB_ROOT_PASSWD` | `_(unset)_` | — | +| `REDIS_HOST` | `127.0.0.1` | Redis config | +| `REDIS_PORT` | `6379` | — | +| `POSTGRES_DB` | `authentik` | Authentik | +| `POSTGRES_USER` | `authentik` | — | +| `POSTGRES_PASSWORD` | `authentik` | — | +| `AUTHENTIK_SECRET_KEY` | `_(unset)_` | — | +| `AUTHENTIK_BOOTSTRAP_PASSWORD` | `_(unset)_` | — | +| `ROMM_AUTH_SECRET_KEY` | `_(unset)_` | Authentication | +| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | `_(unset)_` | — | +| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | `_(unset)_` | — | +| `SESSION_MAX_AGE_SECONDS` | `_(unset)_` | — | +| `DISABLE_DOWNLOAD_ENDPOINT_AUTH` | `_(unset)_` | Disable auth on download endpoint for 3rd party support | +| `DISABLE_CSRF_PROTECTION` | `_(unset)_` | Disable CSRF protection for development and testing purposes | +| `DISABLE_USERPASS_LOGIN` | `_(unset)_` | Disable username + passsword login when using OIDC login | +| `DISABLE_SETUP_WIZARD` | `_(unset)_` | — | +| `INVITE_TOKEN_EXPIRY_SECONDS` | `600` | — | +| `OIDC_ENABLED` | `_(unset)_` | OpenID Connect (Authentik, Authelia, etc.) | +| `OIDC_AUTOLOGIN` | `_(unset)_` | — | +| `OIDC_PROVIDER` | `_(unset)_` | — | +| `OIDC_CLIENT_ID` | `_(unset)_` | — | +| `OIDC_CLIENT_SECRET` | `_(unset)_` | — | +| `OIDC_REDIRECT_URI` | `_(unset)_` | — | +| `OIDC_SERVER_APPLICATION_URL` | `_(unset)_` | — | +| `OIDC_SERVER_METADATA_URL` | `_(unset)_` | — | +| `OIDC_CLAIM_ROLES` | `_(unset)_` | — | +| `OIDC_ROLE_VIEWER` | `_(unset)_` | — | +| `OIDC_ROLE_EDITOR` | `_(unset)_` | — | +| `OIDC_ROLE_ADMIN` | `_(unset)_` | — | +| `OIDC_TLS_CACERTFILE` | `_(unset)_` | — | +| `OIDC_USERNAME_ATTRIBUTE` | `preferred_username` | — | +| `OIDC_RP_INITIATED_LOGOUT` | `_(unset)_` | — | +| `OIDC_END_SESSION_ENDPOINT` | `_(unset)_` | — | +| `ENABLE_RESCAN_ON_FILESYSTEM_CHANGE` | `true` | Filesystem watcher (optional) | +| `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` | `5` | — | +| `TASK_TIMEOUT` | `300` | Tasks (optional) | +| `TASK_RESULT_TTL` | `86400` | — | +| `SEVEN_ZIP_TIMEOUT` | `60` | — | +| `ENABLE_SCHEDULED_RESCAN` | `true` | — | +| `SCHEDULED_RESCAN_CRON` | `0 3 * * *` | — | +| `ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB` | `true` | — | +| `SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON` | `0 4 * * *` | — | +| `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA` | `true` | — | +| `SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON` | `0 4 * * *` | — | +| `ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP` | `true` | — | +| `SCHEDULED_CONVERT_IMAGES_TO_WEBP_CRON` | `0 4 * * *` | — | +| `ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC` | `true` | — | +| `SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC_CRON` | `0 4 * * *` | — | +| `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` | `30` | — | +| `DISABLE_EMULATOR_JS` | `false` | In-browser emulation | +| `DISABLE_RUFFLE_RS` | `false` | — | +| `YOUTUBE_BASE_URL` | `https://www.youtube.com` | YouTube alternatives (Piped, Invidious, etc.) | +| `TINFOIL_WELCOME_MESSAGE` | `"RomM Switch Library"` | Switch Tinfoil | +| `LOGLEVEL` | `DEBUG` | Logging | +| `FORCE_COLOR` | `_(unset)_` | — | +| `NO_COLOR` | `_(unset)_` | — | +| `WEB_SERVER_CONCURRENCY` | `2` | Web server (optional) Workers -> (2 × CPU cores) + 1 | +| `WEB_SERVER_TIMEOUT` | `300` | — | +| `WEB_SERVER_KEEPALIVE` | `2` | — | +| `WEB_SERVER_MAX_REQUESTS` | `1000` | — | +| `WEB_SERVER_MAX_REQUESTS_JITTER` | `100` | — | +| `WEB_SERVER_WORKER_CONNECTIONS` | `1000` | — | +| `WEB_SERVER_GUNICORN_WAIT_SECONDS` | `30` | — | +| `IPV4_ONLY` | `false` | — | +| `SCAN_TIMEOUT` | `_(unset)_` | Redis Workers | +| `SCAN_WORKERS` | `_(unset)_` | — | +| `DEV_MODE` | `true` | Development only | +| `DEV_HOST` | `127.0.0.1` | — | +| `DEV_PORT` | `5000` | — | +| `DEV_HTTPS` | `false` | — | +| `DEV_SQL_ECHO` | `false` | — | +| `SENTRY_DSN` | `_(unset)_` | — | diff --git a/docs/resources/snippets/scheduled-tasks.md b/docs/resources/snippets/scheduled-tasks.md new file mode 100644 index 00000000..3d4f719e --- /dev/null +++ b/docs/resources/snippets/scheduled-tasks.md @@ -0,0 +1,15 @@ + + +| Task | Type | Default schedule | Env var | Purpose | +| --- | --- | --- | --- | --- | +| Folder Scan | Scheduled | `0 0 * * *` | `SCAN_INTERVAL_CRON` | Rescan ROM library for new or changed files. | +| Switch titleDB Fetch | Scheduled | `0 12 * * 0` | `SWITCH_TITLEDB_FETCH_INTERVAL_CRON` | Update Nintendo Switch game database used for matching. | +| LaunchBox Metadata Sync | Scheduled | `0 2 * * *` | `LAUNCHBOX_SYNC_INTERVAL_CRON` | Synchronize LaunchBox metadata cache. | +| Image Conversion | Scheduled | `0 3 * * *` | `IMAGE_CONVERSION_INTERVAL_CRON` | Convert media to WebP for serving. | +| RetroAchievements Sync | Scheduled | `0 4 * * *` | `RETROACHIEVEMENTS_SYNC_INTERVAL_CRON` | Sync per-user RetroAchievements progression data. | +| Netplay Cleanup | Scheduled | `*/30 * * * *` | `NETPLAY_CLEANUP_INTERVAL_CRON` | Remove orphaned netplay sessions. | +| Push-Pull Device Sync | Scheduled | `*/15 * * * *` | `PUSH_PULL_SYNC_INTERVAL_CRON` | Bidirectional save/state sync to registered devices. | +| Cleanup Missing ROMs | Manual | `—` | `—` | Remove DB entries whose files are no longer on disk. | +| Cleanup Orphaned Resources | Manual | `—` | `—` | Delete cached media not referenced by any ROM. | +| Sync Folder Scan | Manual | `—` | `—` | On-demand library scan + sync. | +| Filesystem Watcher | Watcher | `—` | `WATCHER_ENABLED` | Live-watch the library folder; trigger quick scans on changes. | diff --git a/docs/resources/snippets/supported-platforms.md b/docs/resources/snippets/supported-platforms.md new file mode 100644 index 00000000..8c9e0a41 --- /dev/null +++ b/docs/resources/snippets/supported-platforms.md @@ -0,0 +1,6 @@ + + + +_Supported-platforms table is generated at build time. Run +`uv run python docs/scripts/gen_platforms.py` to refresh._ diff --git a/docs/scripts/__pycache__/_sources.cpython-311.pyc b/docs/scripts/__pycache__/_sources.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa9d11738fe01969f5b2b7d5d2d91e31c8f81245 GIT binary patch literal 2714 zcmbVOO>7fK6rSB3+wo6q$d3!DNX$>6PMR3RPf-PJQ=nDa;-m>xg^{{4o(bMy@4B;V zLV^(%s?<_NYQ%*|sFKwKrGi6_RaL3tQuSy@mST;B6qQqMsptXa(!N>$#LLg6v$JpB zzIi*Iec$`$`KQiKAA**={Y3L&gr10pW>e~l^z<)K?jV8^h)6`vNLe`{OF~yNN;gJwkW;&Y75Z7gw(3$X1GTww&$F}B6cx6OA)PGMx=mFgIRthw=_VFG( zJZKqaI-6=DNQ1;Z??)&RqQNaY@dz8~uxGZBUgG~6C$^I?3BuS#UL)IJ>?S)%4~#uT znL^Q>^CMH~B%>sfrkNaNW@Or65!1-C6dg9Ehb@-3(veG4rz~k1%p6%4n}|x>pVW23 zN?Mwsn{jx?6G(ncU5&m1jD~L`zvI zv2wGyL4l>Dr?)}5gRG_<#!@S4ZAL)G8PTKYIRvMqWDzPLr)NqgfHOh&WQaE0 z^(LVyv=<#hCZ3hAD(BIxB%$+2f{VaRRE~3*U5sKLm>U0l^3<6r^~14G&v0zysLnm& zi!<6qjx$D*KyajKQ*1i2`DQF_WN9pKQWhgd%B%}B#k8Dd#?)rSa&tT&UTqp!H@q!w z^CpNQdhG38_4ciJ`<9MWypgImvVkPmKxy=GD7;Wu8ohO6;l_pn7Tdff{uW;U2(e{* znfC12vzM8-Y-R|yakuA$@*5sSo=1uVlH(j?09Qh^oM=a~ZO)nB*QJ6)q@Cbi zZ$#eGuglNPDQ$BK!3Fs)BCdjB%U5NH%ZdA4^n)9abjLYnMm7tQ<_xYdIz7K9ZCN?< zomh+|XGbn+Ryu!CK$bFei|W=$%E-nxrC3z9apCR^e6yB|V%7sY=bi?NYzI6su}>3D z85DuWx`abO{G19)t9h1TyTBwo&Cfspw86ex`hs5ksOIllJht%O(y^+4Pw{lkw|#N{ zLTu@csxMMJRr8N8wO2g3y=-KE43)r^cABMDL%i6 znQxj}skCa*s}>ssyKpn_fDmL1E%dK?2UffTCFTBP**j429;|u~3c3s}pRR?vzn}YV z?$))1YpbE%E1}(`1C`KVH8dy~I%GR-3Bk}I!O-U?wp?tj!O&NGncHD+KOPtjE04l> zw9oTsmjseyJ(;E4(@10{z(}>DEDSbTi=a63qSVR@m})gBYA6XA zf&%#vJM&3G1sSO6il2+h+>?X)q(imnpjS21QjN=OFZ@2>+(C6fP17$$6*!0EV-pi+ z&P}PO$0oTedzomA`(~MDQ5&_W!iL~2_7(`PKysKOTU(s$ta~;p6FUlX``|X;2LTY{ zZL2uEg2PMJeNx8Z3O-!Lhs*fzV_$fwr{dcMl*FEI;y2@qZ)H%lszX>}egeX(?p=R^Mj^xkC4uO68ZFZc<8YG{g@3IfT zQzVu7F9=|RB-PN7GCHz`_LQA{4f%@pRYSeyX0M?gWoKVg+*PG}adt)NFDv~u+*OR% zuQmL7x!Ko(UEh+$= (3, 11): + import tomllib +else: + import tomli as tomllib + +SCRIPTS_DIR = Path(__file__).resolve().parent +SOURCES_FILE = SCRIPTS_DIR / "sources.toml" +SNIPPETS_DIR = SCRIPTS_DIR.parent / "resources" / "snippets" + + +def load_sources() -> dict: + with SOURCES_FILE.open("rb") as f: + return tomllib.load(f) + + +def romm_raw_url(path: str) -> str: + sources = load_sources() + repo = sources["romm"]["repo"] + ref = sources["romm"]["ref"] + return f"https://raw.githubusercontent.com/{repo}/{ref}/{path.lstrip('/')}" + + +def fetch_text(url: str) -> str: + with urllib.request.urlopen(url, timeout=30) as resp: + return resp.read().decode("utf-8") + + +def write_snippet(name: str, content: str) -> Path: + SNIPPETS_DIR.mkdir(parents=True, exist_ok=True) + out = SNIPPETS_DIR / name + out.write_text(content, encoding="utf-8") + return out diff --git a/docs/scripts/check_redirects.py b/docs/scripts/check_redirects.py new file mode 100644 index 00000000..1cdc463e --- /dev/null +++ b/docs/scripts/check_redirects.py @@ -0,0 +1,70 @@ +"""Validate the redirect_maps in mkdocs.yml against the built site. + +Fails CI if any redirect target file does not exist after `mkdocs build`. + +Run manually: + uv run mkdocs build + uv run python docs/scripts/check_redirects.py +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[2] +MKDOCS_YML = REPO_ROOT / "mkdocs.yml" +SITE_DIR = REPO_ROOT / "site" + + +class _LooseLoader(yaml.SafeLoader): + pass + + +def _ignore_python_tags(loader, tag_suffix, node): # noqa: ARG001 + return None + + +_LooseLoader.add_multi_constructor("tag:yaml.org,2002:python/", _ignore_python_tags) + + +def load_redirect_map() -> dict[str, str]: + with MKDOCS_YML.open("r", encoding="utf-8") as f: + cfg = yaml.load(f, Loader=_LooseLoader) + for plugin in cfg.get("plugins", []): + if isinstance(plugin, dict) and "redirects" in plugin: + return plugin["redirects"].get("redirect_maps", {}) + return {} + + +def main() -> int: + if not SITE_DIR.exists(): + print( + f"ERROR: {SITE_DIR} does not exist. Run `mkdocs build` first.", + file=sys.stderr, + ) + return 2 + + redirects = load_redirect_map() + missing: list[tuple[str, str]] = [] + for src, dst in redirects.items(): + # mkdocs-redirects rewrites the source page to redirect to the target. + # Targets are .md paths relative to docs_dir; resolve to built HTML. + html_path = SITE_DIR / dst.replace(".md", "/index.html") + if not html_path.exists(): + missing.append((src, dst)) + + if missing: + print(f"ERROR: {len(missing)} redirect target(s) do not exist in built site:") + for src, dst in missing: + print(f" {src} -> {dst}") + return 1 + + print(f"OK: all {len(redirects)} redirect targets exist.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/gen_env_vars.py b/docs/scripts/gen_env_vars.py new file mode 100644 index 00000000..6f24e316 --- /dev/null +++ b/docs/scripts/gen_env_vars.py @@ -0,0 +1,97 @@ +"""Generate the env-vars reference table from rommapp/romm's env.template. + +Output: docs/resources/snippets/env-vars.md (a Markdown table). + +Included by reference/environment-variables.md via: + --8<-- "env-vars.md" + +Run manually: + uv run python docs/scripts/gen_env_vars.py +""" + +from __future__ import annotations + +import re +import sys +from collections import defaultdict +from typing import Iterable + +from _sources import fetch_text, romm_raw_url, write_snippet + +SECTION_RE = re.compile(r"^#\s*([A-Z][A-Z0-9 _/\-&]+?)\s*#*\s*$") +COMMENT_RE = re.compile(r"^#\s?(.*)$") +ASSIGN_RE = re.compile(r"^([A-Z][A-Z0-9_]*)=(.*)$") + + +def parse(env_template: str) -> list[dict]: + section = "General" + pending_comments: list[str] = [] + rows: list[dict] = [] + + for raw in env_template.splitlines(): + line = raw.rstrip() + if not line.strip(): + pending_comments = [] + continue + + sec = SECTION_RE.match(line) + if sec and len(line) > 4 and line.startswith("#") and "=" not in line: + section = sec.group(1).strip().title() + pending_comments = [] + continue + + cmt = COMMENT_RE.match(line) + if cmt: + pending_comments.append(cmt.group(1).strip()) + continue + + m = ASSIGN_RE.match(line) + if m: + name, default = m.group(1), m.group(2).strip() + rows.append({ + "section": section, + "name": name, + "default": default, + "description": " ".join(c for c in pending_comments if c), + }) + pending_comments = [] + + return rows + + +def render(rows: Iterable[dict]) -> str: + grouped: dict[str, list[dict]] = defaultdict(list) + for r in rows: + grouped[r["section"]].append(r) + + out: list[str] = [] + out.append("") + out.append("") + for section, items in grouped.items(): + out.append(f"### {section}") + out.append("") + out.append("| Variable | Default | Description |") + out.append("| --- | --- | --- |") + for r in items: + default = r["default"] if r["default"] else "_(unset)_" + desc = r["description"] or "—" + out.append(f"| `{r['name']}` | `{default}` | {desc} |") + out.append("") + return "\n".join(out) + + +def main() -> int: + url = romm_raw_url("env.template") + text = fetch_text(url) + rows = parse(text) + if not rows: + print(f"WARN: no env vars parsed from {url}", file=sys.stderr) + return 1 + snippet = render(rows) + out = write_snippet("env-vars.md", snippet) + print(f"Wrote {len(rows)} env vars to {out}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/gen_platforms.py b/docs/scripts/gen_platforms.py new file mode 100644 index 00000000..c4bdfa6c --- /dev/null +++ b/docs/scripts/gen_platforms.py @@ -0,0 +1,37 @@ +"""Generate the supported-platforms table from rommapp/romm. + +Output: docs/resources/snippets/supported-platforms.md + +Strategy: fetch backend/utils/generate_supported_platforms.py from upstream +along with the platform metadata it consumes, then exec it in a sandbox. +For now this is a stub that emits a placeholder snippet; the real exec +needs a copy of the upstream utility's input data layout, which we'll wire +up after pinning a 5.0 SHA in sources.toml. + +Run manually: + uv run python docs/scripts/gen_platforms.py +""" + +from __future__ import annotations + +from _sources import write_snippet + + +PLACEHOLDER = """\ + + + +_Supported-platforms table is generated at build time. Run +`uv run python docs/scripts/gen_platforms.py` to refresh._ +""" + + +def main() -> int: + out = write_snippet("supported-platforms.md", PLACEHOLDER) + print(f"Wrote placeholder to {out}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/gen_scheduled_tasks.py b/docs/scripts/gen_scheduled_tasks.py new file mode 100644 index 00000000..0557df52 --- /dev/null +++ b/docs/scripts/gen_scheduled_tasks.py @@ -0,0 +1,124 @@ +"""Generate the scheduled-tasks reference table from the romm task registry. + +Output: docs/resources/snippets/scheduled-tasks.md + +Strategy: parse the upstream task registration calls (or, once a stable +public registry exists, import it). For now we ship a hand-curated table +to unblock Wave 1; real auto-extraction lands once the upstream registry +shape stabilizes for 5.0. + +Run manually: + uv run python docs/scripts/gen_scheduled_tasks.py +""" + +from __future__ import annotations + +from _sources import write_snippet + + +# Sourced from rommapp/romm@master backend/tasks/* registration as of 4.8.x. +# Refresh against the 5.0 release SHA before GA. +TASKS = [ + { + "name": "Folder Scan", + "type": "Scheduled", + "default_cron": "0 0 * * *", + "env_var": "SCAN_INTERVAL_CRON", + "purpose": "Rescan ROM library for new or changed files.", + }, + { + "name": "Switch titleDB Fetch", + "type": "Scheduled", + "default_cron": "0 12 * * 0", + "env_var": "SWITCH_TITLEDB_FETCH_INTERVAL_CRON", + "purpose": "Update Nintendo Switch game database used for matching.", + }, + { + "name": "LaunchBox Metadata Sync", + "type": "Scheduled", + "default_cron": "0 2 * * *", + "env_var": "LAUNCHBOX_SYNC_INTERVAL_CRON", + "purpose": "Synchronize LaunchBox metadata cache.", + }, + { + "name": "Image Conversion", + "type": "Scheduled", + "default_cron": "0 3 * * *", + "env_var": "IMAGE_CONVERSION_INTERVAL_CRON", + "purpose": "Convert media to WebP for serving.", + }, + { + "name": "RetroAchievements Sync", + "type": "Scheduled", + "default_cron": "0 4 * * *", + "env_var": "RETROACHIEVEMENTS_SYNC_INTERVAL_CRON", + "purpose": "Sync per-user RetroAchievements progression data.", + }, + { + "name": "Netplay Cleanup", + "type": "Scheduled", + "default_cron": "*/30 * * * *", + "env_var": "NETPLAY_CLEANUP_INTERVAL_CRON", + "purpose": "Remove orphaned netplay sessions.", + }, + { + "name": "Push-Pull Device Sync", + "type": "Scheduled", + "default_cron": "*/15 * * * *", + "env_var": "PUSH_PULL_SYNC_INTERVAL_CRON", + "purpose": "Bidirectional save/state sync to registered devices.", + }, + { + "name": "Cleanup Missing ROMs", + "type": "Manual", + "default_cron": "—", + "env_var": "—", + "purpose": "Remove DB entries whose files are no longer on disk.", + }, + { + "name": "Cleanup Orphaned Resources", + "type": "Manual", + "default_cron": "—", + "env_var": "—", + "purpose": "Delete cached media not referenced by any ROM.", + }, + { + "name": "Sync Folder Scan", + "type": "Manual", + "default_cron": "—", + "env_var": "—", + "purpose": "On-demand library scan + sync.", + }, + { + "name": "Filesystem Watcher", + "type": "Watcher", + "default_cron": "—", + "env_var": "WATCHER_ENABLED", + "purpose": "Live-watch the library folder; trigger quick scans on changes.", + }, +] + + +def render() -> str: + out = [ + "", + "", + "| Task | Type | Default schedule | Env var | Purpose |", + "| --- | --- | --- | --- | --- |", + ] + for t in TASKS: + out.append( + f"| {t['name']} | {t['type']} | `{t['default_cron']}` | `{t['env_var']}` | {t['purpose']} |" + ) + out.append("") + return "\n".join(out) + + +def main() -> int: + out = write_snippet("scheduled-tasks.md", render()) + print(f"Wrote {len(TASKS)} tasks to {out}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/scaffold_ia.py b/docs/scripts/scaffold_ia.py new file mode 100644 index 00000000..e2c3c893 --- /dev/null +++ b/docs/scripts/scaffold_ia.py @@ -0,0 +1,167 @@ +"""One-shot scaffolder: create placeholder pages for the v5.0 IA. + +Idempotent: pages that already exist are left alone. Used during the +initial overhaul setup; can be deleted once the IA is stable. + +Run: + uv run python docs/scripts/scaffold_ia.py +""" + +from __future__ import annotations + +from pathlib import Path + +DOCS = Path(__file__).resolve().parents[1] + +# (relative_path, page_title, wave) -- wave is informational, surfaced in the stub. +PAGES: list[tuple[str, str, int]] = [ + # Getting Started + ("getting-started/quick-start.md", "Quick Start", 1), + ("getting-started/folder-structure.md", "Folder Structure", 1), + ("getting-started/first-scan.md", "Your First Scan", 1), + ("getting-started/concepts.md", "Core Concepts", 1), + ("getting-started/what-is-new-in-5.md", "What's New in 5.0", 1), + # Install & Deploy + ("install/index.md", "Install & Deploy", 1), + ("install/docker-compose.md", "Docker Compose", 1), + ("install/image-variants.md", "Image Variants", 1), + ("install/databases.md", "Databases", 1), + ("install/redis-or-valkey.md", "Redis or Valkey", 1), + ("install/reverse-proxy.md", "Reverse Proxy", 1), + ("install/unraid.md", "Unraid", 1), + ("install/synology.md", "Synology", 1), + ("install/truenas.md", "TrueNAS", 1), + ("install/kubernetes.md", "Kubernetes", 1), + ("install/backup-and-restore.md", "Backup & Restore", 1), + # Administration + ("administration/index.md", "Administration", 1), + ("administration/users-and-roles.md", "Users & Roles", 1), + ("administration/invitations-and-registration.md", "Invitations & Registration", 1), + ("administration/authentication.md", "Authentication", 1), + ("administration/metadata-providers.md", "Metadata Providers", 1), + ("administration/scanning-and-watcher.md", "Scanning & Watcher", 1), + ("administration/scheduled-tasks.md", "Scheduled Tasks", 1), + ("administration/server-stats.md", "Server Stats", 1), + ("administration/observability.md", "Observability", 1), + ("administration/firmware-management.md", "Firmware Management", 1), + ("administration/ssh-sync.md", "SSH Sync", 1), + ("administration/administration-page.md", "Administration Page", 1), + # Administration / OIDC + ("administration/oidc/index.md", "OIDC Setup", 1), + ("administration/oidc/authelia.md", "OIDC with Authelia", 1), + ("administration/oidc/authentik.md", "OIDC with Authentik", 1), + ("administration/oidc/keycloak.md", "OIDC with Keycloak", 1), + ("administration/oidc/pocketid.md", "OIDC with PocketID", 1), + ("administration/oidc/zitadel.md", "OIDC with Zitadel", 1), + # Using RomM + ("using/index.md", "Using RomM", 2), + ("using/library.md", "Library", 2), + ("using/collections.md", "Collections", 2), + ("using/smart-collections.md", "Smart Collections", 2), + ("using/virtual-collections.md", "Virtual Collections", 2), + ("using/downloads.md", "Downloads", 2), + ("using/uploads.md", "Uploads", 2), + ("using/in-browser-play.md", "In-Browser Play", 2), + ("using/saves-and-states.md", "Saves & States", 2), + ("using/retroachievements.md", "RetroAchievements", 2), + ("using/rom-patcher.md", "ROM Patcher", 2), + ("using/netplay.md", "Netplay", 2), + ("using/console-mode.md", "Console Mode", 2), + ("using/pwa.md", "Install as PWA", 2), + ("using/mobile-and-tv.md", "Mobile & TV", 2), + ("using/account-and-profile.md", "Account & Profile", 2), + ("using/languages.md", "Languages", 2), + # Platforms & Players + ("platforms/index.md", "Platforms & Players", 2), + ("platforms/supported-platforms.md", "Supported Platforms", 2), + ("platforms/custom-platforms.md", "Custom Platforms", 2), + ("platforms/ms-dos.md", "MS-DOS", 2), + ("platforms/emulatorjs-config.md", "EmulatorJS Configuration", 2), + ("platforms/ruffle-config.md", "Ruffle Configuration", 2), + ("platforms/firmware-by-platform.md", "Firmware by Platform", 2), + # Ecosystem + ("ecosystem/index.md", "Integrations & Ecosystem", 3), + ("ecosystem/argosy.md", "Argosy Launcher", 3), + ("ecosystem/grout.md", "Grout", 3), + ("ecosystem/playnite-plugin.md", "Playnite Plugin", 3), + ("ecosystem/muos-app.md", "muOS App", 3), + ("ecosystem/tinfoil.md", "Tinfoil", 3), + ("ecosystem/pkgj.md", "pkgj", 3), + ("ecosystem/fpkgi.md", "fpkgi", 3), + ("ecosystem/kekatsu.md", "Kekatsu", 3), + ("ecosystem/webrcade.md", "WebRcade", 3), + ("ecosystem/community-apps.md", "Community Apps", 3), + ("ecosystem/device-sync-protocol.md", "Device Sync Protocol", 3), + ("ecosystem/client-api-tokens.md", "Client API Tokens", 3), + ("ecosystem/igir.md", "Igir Collection Manager", 3), + # Developers + ("developers/index.md", "API & Development", 1), + ("developers/api-reference.md", "API Reference", 1), + ("developers/api-authentication.md", "API Authentication", 1), + ("developers/websockets.md", "WebSockets", 3), + ("developers/openapi.md", "Consuming OpenAPI", 3), + ("developers/development-setup.md", "Development Setup", 1), + ("developers/architecture.md", "Architecture", 3), + ("developers/contributing.md", "Contributing", 1), + ("developers/i18n.md", "Translations (i18n)", 3), + ("developers/releasing.md", "Releasing", 3), + # Reference + ("reference/environment-variables.md", "Environment Variables", 1), + ("reference/configuration-file.md", "Configuration File", 1), + ("reference/scheduled-tasks.md", "Scheduled Tasks Reference", 1), + ("reference/exports.md", "Exports", 3), + ("reference/feeds.md", "Feeds", 3), + ("reference/ports-and-endpoints.md", "Ports & Endpoints", 3), + ("reference/glossary.md", "Glossary", 3), + # Troubleshooting + ("troubleshooting/index.md", "Troubleshooting", 1), + ("troubleshooting/scanning.md", "Scanning", 1), + ("troubleshooting/authentication.md", "Authentication", 1), + ("troubleshooting/synology.md", "Synology", 1), + ("troubleshooting/kubernetes.md", "Kubernetes", 1), + ("troubleshooting/in-browser-play.md", "In-Browser Play", 2), + ("troubleshooting/netplay.md", "Netplay", 2), + ("troubleshooting/sync.md", "Device Sync", 2), + ("troubleshooting/miscellaneous.md", "Miscellaneous", 1), + # Releases + ("releases/index.md", "Release Notes & Migration", 1), + ("releases/upgrading-to-5.0.md", "Upgrading to 5.0", 1), + ("releases/upgrading-to-3.0.md", "Upgrading to 3.0", 1), + ("releases/changelog.md", "Changelog", 1), + # About + ("about/faqs.md", "FAQs", 3), + ("about/brand-guidelines.md", "Brand Guidelines", 3), + ("about/license.md", "License", 3), + ("about/credits.md", "Credits", 3), +] + + +STUB = """\ +--- +status: placeholder +wave: {wave} +--- + +# {title} + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave {wave}) and + has not been written yet. See the overhaul plan for status and ownership. +""" + + +def main() -> int: + created = 0 + for rel, title, wave in PAGES: + path = DOCS / rel + if path.exists(): + continue + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(STUB.format(title=title, wave=wave), encoding="utf-8") + created += 1 + print(f"Created {created} placeholder pages ({len(PAGES) - created} already existed).") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/sources.toml b/docs/scripts/sources.toml new file mode 100644 index 00000000..0747e9f1 --- /dev/null +++ b/docs/scripts/sources.toml @@ -0,0 +1,10 @@ +# Pinned upstream sources for the docs generators. +# +# Bumped automatically by .github/workflows/romm-release-bump.yml on each +# rommapp/romm release. Do not hand-edit unless you know why. + +[romm] +repo = "rommapp/romm" +# Set to a tag (e.g. "v5.0.0") or a full SHA. Generators fetch raw files +# from raw.githubusercontent.com/${repo}/${ref}/... +ref = "master" diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md new file mode 100644 index 00000000..c133a664 --- /dev/null +++ b/docs/troubleshooting/authentication.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Authentication + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/in-browser-play.md b/docs/troubleshooting/in-browser-play.md new file mode 100644 index 00000000..73ce0f3a --- /dev/null +++ b/docs/troubleshooting/in-browser-play.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# In-Browser Play + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md new file mode 100644 index 00000000..91a87636 --- /dev/null +++ b/docs/troubleshooting/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Troubleshooting + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/kubernetes.md b/docs/troubleshooting/kubernetes.md new file mode 100644 index 00000000..6d9abd12 --- /dev/null +++ b/docs/troubleshooting/kubernetes.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Kubernetes + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/miscellaneous.md b/docs/troubleshooting/miscellaneous.md new file mode 100644 index 00000000..3a4e49bb --- /dev/null +++ b/docs/troubleshooting/miscellaneous.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Miscellaneous + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/netplay.md b/docs/troubleshooting/netplay.md new file mode 100644 index 00000000..d5181aa9 --- /dev/null +++ b/docs/troubleshooting/netplay.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Netplay + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/scanning.md b/docs/troubleshooting/scanning.md new file mode 100644 index 00000000..232cf044 --- /dev/null +++ b/docs/troubleshooting/scanning.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Scanning + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md new file mode 100644 index 00000000..ab83d5e3 --- /dev/null +++ b/docs/troubleshooting/sync.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Device Sync + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/troubleshooting/synology.md b/docs/troubleshooting/synology.md new file mode 100644 index 00000000..ca418eb9 --- /dev/null +++ b/docs/troubleshooting/synology.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 1 +--- + +# Synology + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 1) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md new file mode 100644 index 00000000..30d09b25 --- /dev/null +++ b/docs/using/account-and-profile.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Account & Profile + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/collections.md b/docs/using/collections.md new file mode 100644 index 00000000..b419c1bd --- /dev/null +++ b/docs/using/collections.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Collections + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md new file mode 100644 index 00000000..ad6bd22a --- /dev/null +++ b/docs/using/console-mode.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Console Mode + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/downloads.md b/docs/using/downloads.md new file mode 100644 index 00000000..eaefbdc9 --- /dev/null +++ b/docs/using/downloads.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Downloads + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md new file mode 100644 index 00000000..73ce0f3a --- /dev/null +++ b/docs/using/in-browser-play.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# In-Browser Play + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/index.md b/docs/using/index.md new file mode 100644 index 00000000..2489e7da --- /dev/null +++ b/docs/using/index.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Using RomM + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/languages.md b/docs/using/languages.md new file mode 100644 index 00000000..5d267fb4 --- /dev/null +++ b/docs/using/languages.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Languages + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/library.md b/docs/using/library.md new file mode 100644 index 00000000..f6d73ad4 --- /dev/null +++ b/docs/using/library.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Library + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/mobile-and-tv.md b/docs/using/mobile-and-tv.md new file mode 100644 index 00000000..187be756 --- /dev/null +++ b/docs/using/mobile-and-tv.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Mobile & TV + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/netplay.md b/docs/using/netplay.md new file mode 100644 index 00000000..d5181aa9 --- /dev/null +++ b/docs/using/netplay.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Netplay + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/pwa.md b/docs/using/pwa.md new file mode 100644 index 00000000..32059f6a --- /dev/null +++ b/docs/using/pwa.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Install as PWA + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/retroachievements.md b/docs/using/retroachievements.md new file mode 100644 index 00000000..a69e4c2c --- /dev/null +++ b/docs/using/retroachievements.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# RetroAchievements + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/rom-patcher.md b/docs/using/rom-patcher.md new file mode 100644 index 00000000..736c48ae --- /dev/null +++ b/docs/using/rom-patcher.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# ROM Patcher + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md new file mode 100644 index 00000000..74cff38e --- /dev/null +++ b/docs/using/saves-and-states.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Saves & States + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/smart-collections.md b/docs/using/smart-collections.md new file mode 100644 index 00000000..19dec0cf --- /dev/null +++ b/docs/using/smart-collections.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Smart Collections + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/uploads.md b/docs/using/uploads.md new file mode 100644 index 00000000..bf92838a --- /dev/null +++ b/docs/using/uploads.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Uploads + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/docs/using/virtual-collections.md b/docs/using/virtual-collections.md new file mode 100644 index 00000000..7a2332ae --- /dev/null +++ b/docs/using/virtual-collections.md @@ -0,0 +1,10 @@ +--- +status: placeholder +wave: 2 +--- + +# Virtual Collections + +!!! warning "Placeholder — RomM 5.0 docs overhaul" + This page is part of the RomM 5.0 documentation overhaul (Wave 2) and + has not been written yet. See the overhaul plan for status and ownership. diff --git a/mkdocs.yml b/mkdocs.yml index fcb33706..c55025b1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -52,7 +52,7 @@ plugins: font_family: Roboto background_color: "#371f69" color: "#ede5f8" - logo: docs/assets/romm/romm-logo.pngg + logo: docs/assets/romm/romm-logo.png title: RomM Docs description: Your beautiful, powerful, self-hosted rom manager - glightbox: @@ -65,6 +65,63 @@ plugins: enable_creation_date: true type: timeago - mike + - redirects: + redirect_maps: + # Getting Started → new IA + Getting-Started/Quick-Start-Guide.md: getting-started/quick-start.md + Getting-Started/Folder-Structure.md: getting-started/folder-structure.md + Getting-Started/Configuration-File.md: reference/configuration-file.md + Getting-Started/Environment-Variables.md: reference/environment-variables.md + Getting-Started/Metadata-Providers.md: administration/metadata-providers.md + Getting-Started/OIDC-Setup.md: administration/oidc/index.md + Getting-Started/Reverse-Proxy.md: install/reverse-proxy.md + Getting-Started/Authentication.md: administration/authentication.md + # Platforms and Players + Platforms-and-Players/Supported-Platforms.md: platforms/supported-platforms.md + Platforms-and-Players/EmulatorJS-Player.md: using/in-browser-play.md + Platforms-and-Players/RuffleRS-Player.md: using/in-browser-play.md + Platforms-and-Players/MS-DOS.md: platforms/ms-dos.md + Platforms-and-Players/Custom-Platforms.md: platforms/custom-platforms.md + # System Setup → Install + System-Setup/Unraid-Setup-Guide.md: install/unraid.md + System-Setup/Unraid-Compose-Setup.md: install/unraid.md + System-Setup/Synology-Setup-Guide.md: install/synology.md + System-Setup/TrueNAS-Setup-Guide.md: install/truenas.md + # Usage → split into Administration + Using + Usage/LibraryManagement.md: using/library.md + Usage/UserManagement.md: administration/users-and-roles.md + Usage/Administration.md: administration/administration-page.md + # OIDC Guides + OIDC-Guides/OIDC-Setup-With-Authelia.md: administration/oidc/authelia.md + OIDC-Guides/OIDC-Setup-With-Authentik.md: administration/oidc/authentik.md + OIDC-Guides/OIDC-Setup-With-Keycloak.md: administration/oidc/keycloak.md + OIDC-Guides/OIDC-Setup-With-PocketID.md: administration/oidc/pocketid.md + OIDC-Guides/OIDC-Setup-With-Zitadel.md: administration/oidc/zitadel.md + # API & Development + API-and-Development/index.md: developers/index.md + API-and-Development/API-Reference.md: developers/api-reference.md + API-and-Development/Development-Setup.md: developers/development-setup.md + API-and-Development/Contributing.md: developers/contributing.md + # Tools → Ecosystem + Tools/Igir-Collection-Manager.md: ecosystem/igir.md + # Maintenance + Maintenance/Scheduled-Tasks.md: reference/scheduled-tasks.md + Maintenance/Migrating-to-new-machine.md: install/backup-and-restore.md + Maintenance/Upgrading-to-3.0.md: releases/upgrading-to-3.0.md + # Integrations → Ecosystem + Integrations/Playnite-plugin.md: ecosystem/playnite-plugin.md + Integrations/muOS-app.md: ecosystem/muos-app.md + Integrations/Tinfoil-integration.md: ecosystem/tinfoil.md + Integrations/pkgj-integration.md: ecosystem/pkgj.md + # Troubleshooting (just casing change) + Troubleshooting/Scanning-Issues.md: troubleshooting/scanning.md + Troubleshooting/Authentication-Issues.md: troubleshooting/authentication.md + Troubleshooting/Synology-Issues.md: troubleshooting/synology.md + Troubleshooting/Miscellaneous-Troubleshooting.md: troubleshooting/miscellaneous.md + Troubleshooting/Kubernetes-Issues.md: troubleshooting/kubernetes.md + # Miscellaneous → About + Miscellaneous/FAQs.md: about/faqs.md + Miscellaneous/Brand-Guidelines.md: about/brand-guidelines.md markdown_extensions: - pymdownx.blocks.caption diff --git a/pyproject.toml b/pyproject.toml index 3ab5f42f..949f80a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,8 +13,10 @@ dependencies = [ "mkdocs-literate-nav>=0.6.1", "mkdocs-markdownextradata-plugin>=0.2.6", "mkdocs-material>=9.6.5", + "mkdocs-redirects>=1.2.2", "mkdocs-video>=1.5.0", - "mike>=2.1.3", + "neoteroi-mkdocs>=1.1.0", "pillow>=11.1.0", "mkdocs-asciinema-player>=0.16.0", + "tomli>=2.0.1", ] diff --git a/uv.lock b/uv.lock index 9f7a9476..45427146 100644 --- a/uv.lock +++ b/uv.lock @@ -1,14 +1,27 @@ version = 1 -revision = 1 +revision = 3 requires-python = ">=3.12" +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + [[package]] name = "babel" version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, ] [[package]] @@ -18,9 +31,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/c5/1a4dc131459e68a173cbdab5fad6b524f53f9c1ef7861b7698e998b837cc/cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b", size = 88096 } +sdist = { url = "https://files.pythonhosted.org/packages/70/c5/1a4dc131459e68a173cbdab5fad6b524f53f9c1ef7861b7698e998b837cc/cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b", size = 88096, upload-time = "2024-06-18T10:56:06.741Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d8/ba13451aa6b745c49536e87b6bf8f629b950e84bd0e8308f7dc6883b67e2/cairocffi-1.7.1-py3-none-any.whl", hash = "sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f", size = 75611 }, + { url = "https://files.pythonhosted.org/packages/93/d8/ba13451aa6b745c49536e87b6bf8f629b950e84bd0e8308f7dc6883b67e2/cairocffi-1.7.1-py3-none-any.whl", hash = "sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f", size = 75611, upload-time = "2024-06-18T10:55:59.489Z" }, ] [[package]] @@ -34,18 +47,18 @@ dependencies = [ { name = "pillow" }, { name = "tinycss2" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/e6/ec5900b724e3c44af7f6f51f719919137284e5da4aabe96508baec8a1b40/CairoSVG-2.7.1.tar.gz", hash = "sha256:432531d72347291b9a9ebfb6777026b607563fd8719c46ee742db0aef7271ba0", size = 8399085 } +sdist = { url = "https://files.pythonhosted.org/packages/d5/e6/ec5900b724e3c44af7f6f51f719919137284e5da4aabe96508baec8a1b40/CairoSVG-2.7.1.tar.gz", hash = "sha256:432531d72347291b9a9ebfb6777026b607563fd8719c46ee742db0aef7271ba0", size = 8399085, upload-time = "2023-08-05T09:08:05.75Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/a5/1866b42151f50453f1a0d28fc4c39f5be5f412a2e914f33449c42daafdf1/CairoSVG-2.7.1-py3-none-any.whl", hash = "sha256:8a5222d4e6c3f86f1f7046b63246877a63b49923a1cd202184c3a634ef546b3b", size = 43235 }, + { url = "https://files.pythonhosted.org/packages/01/a5/1866b42151f50453f1a0d28fc4c39f5be5f412a2e914f33449c42daafdf1/CairoSVG-2.7.1-py3-none-any.whl", hash = "sha256:8a5222d4e6c3f86f1f7046b63246877a63b49923a1cd202184c3a634ef546b3b", size = 43235, upload-time = "2023-08-05T09:08:01.583Z" }, ] [[package]] name = "certifi" version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" }, ] [[package]] @@ -55,65 +68,65 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, - { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, - { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, - { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, - { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, - { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, - { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, - { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, - { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, - { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, - { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, - { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, - { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, - { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, - { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, - { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, - { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, - { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, - { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, - { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, - { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, - { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, - { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, - { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, - { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, - { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, - { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, ] [[package]] @@ -123,18 +136,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -145,18 +158,41 @@ dependencies = [ { name = "tinycss2" }, { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/fc/326cb6f988905998f09bb54a3f5d98d4462ba119363c0dfad29750d48c09/cssselect2-0.7.0.tar.gz", hash = "sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a", size = 35888 } +sdist = { url = "https://files.pythonhosted.org/packages/e7/fc/326cb6f988905998f09bb54a3f5d98d4462ba119363c0dfad29750d48c09/cssselect2-0.7.0.tar.gz", hash = "sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a", size = 35888, upload-time = "2022-09-19T12:55:11.876Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/3a/e39436efe51894243ff145a37c4f9a030839b97779ebcc4f13b3ba21c54e/cssselect2-0.7.0-py3-none-any.whl", hash = "sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969", size = 15586 }, + { url = "https://files.pythonhosted.org/packages/9d/3a/e39436efe51894243ff145a37c4f9a030839b97779ebcc4f13b3ba21c54e/cssselect2-0.7.0-py3-none-any.whl", hash = "sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969", size = 15586, upload-time = "2022-09-19T12:55:07.56Z" }, ] [[package]] name = "defusedxml" version = "0.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, +] + +[[package]] +name = "essentials" +version = "1.1.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/ff/587c8789abad9c76ef2a5bf613c6d7922a0d09879e3eda1864d108f11b75/essentials-1.1.9.tar.gz", hash = "sha256:7fbea3a518cbeafe5374fb7e2ea2c15a109e8a7fd1eaab62ae87cbd1b3b1e8d0", size = 25068, upload-time = "2025-11-23T20:34:46.533Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, + { url = "https://files.pythonhosted.org/packages/50/8b/68aa342fa78931429ea09e6dc8d9ffa46fa36cfb0cf423e612d22365a9c2/essentials-1.1.9-py2.py3-none-any.whl", hash = "sha256:71ef161e0e27ef77cd6f5fc05e0b8688a575fcab870c01c95940f832e321dfbb", size = 16151, upload-time = "2025-11-23T20:34:45.025Z" }, +] + +[[package]] +name = "essentials-openapi" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "essentials" }, + { name = "markupsafe" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d6/e1ddfdd454011e05d2c73f0f21b1ad6e1a8e125a36048f855a2374c4d46a/essentials_openapi-1.4.0.tar.gz", hash = "sha256:578c81501ccf6d18c0839d60636214fbd051f0ef37f1d207d4e3c92de2aac008", size = 37291, upload-time = "2026-03-07T09:33:44.989Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6a/612ec427b5153f0d80dd9c344f80018bc1c7414e475a20efc0b89e1fe62e/essentials_openapi-1.4.0-py3-none-any.whl", hash = "sha256:86d879c32734248ad52482a90ee89a32883bce348b4edd323c01556b505b45b9", size = 57673, upload-time = "2026-03-07T09:33:43.828Z" }, ] [[package]] @@ -166,9 +202,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, ] [[package]] @@ -178,9 +214,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smmap" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, ] [[package]] @@ -190,18 +226,55 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196, upload-time = "2025-01-02T07:32:43.59Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599, upload-time = "2025-01-02T07:32:40.731Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] @@ -211,18 +284,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767, upload-time = "2025-01-20T22:21:30.429Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971, upload-time = "2025-01-20T22:21:29.177Z" }, ] [[package]] name = "importlib-resources" version = "6.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461 }, + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, ] [[package]] @@ -232,107 +305,128 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674, upload-time = "2024-12-21T18:30:22.828Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596, upload-time = "2024-12-21T18:30:19.133Z" }, ] [[package]] name = "lxml" version = "5.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/f6/c15ca8e5646e937c148e147244817672cf920b56ac0bf2cc1512ae674be8/lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8", size = 3678591 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/f4/5121aa9ee8e09b8b8a28cf3709552efe3d206ca51a20d6fa471b60bb3447/lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c", size = 8191889 }, - { url = "https://files.pythonhosted.org/packages/0a/ca/8e9aa01edddc74878f4aea85aa9ab64372f46aa804d1c36dda861bf9eabf/lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe", size = 4450685 }, - { url = "https://files.pythonhosted.org/packages/b2/b3/ea40a5c98619fbd7e9349df7007994506d396b97620ced34e4e5053d3734/lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9", size = 5051722 }, - { url = "https://files.pythonhosted.org/packages/3a/5e/375418be35f8a695cadfe7e7412f16520e62e24952ed93c64c9554755464/lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a", size = 4786661 }, - { url = "https://files.pythonhosted.org/packages/79/7c/d258eaaa9560f6664f9b426a5165103015bee6512d8931e17342278bad0a/lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0", size = 5311766 }, - { url = "https://files.pythonhosted.org/packages/03/bc/a041415be4135a1b3fdf017a5d873244cc16689456166fbdec4b27fba153/lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7", size = 4836014 }, - { url = "https://files.pythonhosted.org/packages/32/88/047f24967d5e3fc97848ea2c207eeef0f16239cdc47368c8b95a8dc93a33/lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae", size = 4961064 }, - { url = "https://files.pythonhosted.org/packages/3d/b5/ecf5a20937ecd21af02c5374020f4e3a3538e10a32379a7553fca3d77094/lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519", size = 4778341 }, - { url = "https://files.pythonhosted.org/packages/a4/05/56c359e07275911ed5f35ab1d63c8cd3360d395fb91e43927a2ae90b0322/lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322", size = 5345450 }, - { url = "https://files.pythonhosted.org/packages/b7/f4/f95e3ae12e9f32fbcde00f9affa6b0df07f495117f62dbb796a9a31c84d6/lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468", size = 4908336 }, - { url = "https://files.pythonhosted.org/packages/c5/f8/309546aec092434166a6e11c7dcecb5c2d0a787c18c072d61e18da9eba57/lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367", size = 4986049 }, - { url = "https://files.pythonhosted.org/packages/71/1c/b951817cb5058ca7c332d012dfe8bc59dabd0f0a8911ddd7b7ea8e41cfbd/lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd", size = 4860351 }, - { url = "https://files.pythonhosted.org/packages/31/23/45feba8dae1d35fcca1e51b051f59dc4223cbd23e071a31e25f3f73938a8/lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c", size = 5421580 }, - { url = "https://files.pythonhosted.org/packages/61/69/be245d7b2dbef81c542af59c97fcd641fbf45accf2dc1c325bae7d0d014c/lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f", size = 5285778 }, - { url = "https://files.pythonhosted.org/packages/69/06/128af2ed04bac99b8f83becfb74c480f1aa18407b5c329fad457e08a1bf4/lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645", size = 5054455 }, - { url = "https://files.pythonhosted.org/packages/8a/2d/f03a21cf6cc75cdd083563e509c7b6b159d761115c4142abb5481094ed8c/lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5", size = 3486315 }, - { url = "https://files.pythonhosted.org/packages/2b/9c/8abe21585d20ef70ad9cec7562da4332b764ed69ec29b7389d23dfabcea0/lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf", size = 3816925 }, - { url = "https://files.pythonhosted.org/packages/94/1c/724931daa1ace168e0237b929e44062545bf1551974102a5762c349c668d/lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e", size = 8171881 }, - { url = "https://files.pythonhosted.org/packages/67/0c/857b8fb6010c4246e66abeebb8639eaabba60a6d9b7c606554ecc5cbf1ee/lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd", size = 4440394 }, - { url = "https://files.pythonhosted.org/packages/61/72/c9e81de6a000f9682ccdd13503db26e973b24c68ac45a7029173237e3eed/lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7", size = 5037860 }, - { url = "https://files.pythonhosted.org/packages/24/26/942048c4b14835711b583b48cd7209bd2b5f0b6939ceed2381a494138b14/lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414", size = 4782513 }, - { url = "https://files.pythonhosted.org/packages/e2/65/27792339caf00f610cc5be32b940ba1e3009b7054feb0c4527cebac228d4/lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e", size = 5305227 }, - { url = "https://files.pythonhosted.org/packages/18/e1/25f7aa434a4d0d8e8420580af05ea49c3e12db6d297cf5435ac0a054df56/lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1", size = 4829846 }, - { url = "https://files.pythonhosted.org/packages/fe/ed/faf235e0792547d24f61ee1448159325448a7e4f2ab706503049d8e5df19/lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5", size = 4949495 }, - { url = "https://files.pythonhosted.org/packages/e5/e1/8f572ad9ed6039ba30f26dd4c2c58fb90f79362d2ee35ca3820284767672/lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423", size = 4773415 }, - { url = "https://files.pythonhosted.org/packages/a3/75/6b57166b9d1983dac8f28f354e38bff8d6bcab013a241989c4d54c72701b/lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20", size = 5337710 }, - { url = "https://files.pythonhosted.org/packages/cc/71/4aa56e2daa83bbcc66ca27b5155be2f900d996f5d0c51078eaaac8df9547/lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8", size = 4897362 }, - { url = "https://files.pythonhosted.org/packages/65/10/3fa2da152cd9b49332fd23356ed7643c9b74cad636ddd5b2400a9730d12b/lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9", size = 4977795 }, - { url = "https://files.pythonhosted.org/packages/de/d2/e1da0f7b20827e7b0ce934963cb6334c1b02cf1bb4aecd218c4496880cb3/lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c", size = 4858104 }, - { url = "https://files.pythonhosted.org/packages/a5/35/063420e1b33d3308f5aa7fcbdd19ef6c036f741c9a7a4bd5dc8032486b27/lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b", size = 5416531 }, - { url = "https://files.pythonhosted.org/packages/c3/83/93a6457d291d1e37adfb54df23498101a4701834258c840381dd2f6a030e/lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5", size = 5273040 }, - { url = "https://files.pythonhosted.org/packages/39/25/ad4ac8fac488505a2702656550e63c2a8db3a4fd63db82a20dad5689cecb/lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252", size = 5050951 }, - { url = "https://files.pythonhosted.org/packages/82/74/f7d223c704c87e44b3d27b5e0dde173a2fcf2e89c0524c8015c2b3554876/lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78", size = 3485357 }, - { url = "https://files.pythonhosted.org/packages/80/83/8c54533b3576f4391eebea88454738978669a6cad0d8e23266224007939d/lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332", size = 3814484 }, +sdist = { url = "https://files.pythonhosted.org/packages/ef/f6/c15ca8e5646e937c148e147244817672cf920b56ac0bf2cc1512ae674be8/lxml-5.3.1.tar.gz", hash = "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8", size = 3678591, upload-time = "2025-02-10T07:51:41.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/f4/5121aa9ee8e09b8b8a28cf3709552efe3d206ca51a20d6fa471b60bb3447/lxml-5.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c", size = 8191889, upload-time = "2025-02-10T07:45:41.412Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ca/8e9aa01edddc74878f4aea85aa9ab64372f46aa804d1c36dda861bf9eabf/lxml-5.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe", size = 4450685, upload-time = "2025-02-10T07:45:44.175Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b3/ea40a5c98619fbd7e9349df7007994506d396b97620ced34e4e5053d3734/lxml-5.3.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9", size = 5051722, upload-time = "2025-02-10T07:45:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/3a/5e/375418be35f8a695cadfe7e7412f16520e62e24952ed93c64c9554755464/lxml-5.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a", size = 4786661, upload-time = "2025-02-10T07:45:51.155Z" }, + { url = "https://files.pythonhosted.org/packages/79/7c/d258eaaa9560f6664f9b426a5165103015bee6512d8931e17342278bad0a/lxml-5.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0", size = 5311766, upload-time = "2025-02-10T07:45:54.049Z" }, + { url = "https://files.pythonhosted.org/packages/03/bc/a041415be4135a1b3fdf017a5d873244cc16689456166fbdec4b27fba153/lxml-5.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7", size = 4836014, upload-time = "2025-02-10T07:45:57.699Z" }, + { url = "https://files.pythonhosted.org/packages/32/88/047f24967d5e3fc97848ea2c207eeef0f16239cdc47368c8b95a8dc93a33/lxml-5.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae", size = 4961064, upload-time = "2025-02-10T07:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b5/ecf5a20937ecd21af02c5374020f4e3a3538e10a32379a7553fca3d77094/lxml-5.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519", size = 4778341, upload-time = "2025-02-10T07:46:03.661Z" }, + { url = "https://files.pythonhosted.org/packages/a4/05/56c359e07275911ed5f35ab1d63c8cd3360d395fb91e43927a2ae90b0322/lxml-5.3.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322", size = 5345450, upload-time = "2025-02-10T07:46:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/b7/f4/f95e3ae12e9f32fbcde00f9affa6b0df07f495117f62dbb796a9a31c84d6/lxml-5.3.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468", size = 4908336, upload-time = "2025-02-10T07:46:14.338Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f8/309546aec092434166a6e11c7dcecb5c2d0a787c18c072d61e18da9eba57/lxml-5.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367", size = 4986049, upload-time = "2025-02-10T07:46:18.217Z" }, + { url = "https://files.pythonhosted.org/packages/71/1c/b951817cb5058ca7c332d012dfe8bc59dabd0f0a8911ddd7b7ea8e41cfbd/lxml-5.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd", size = 4860351, upload-time = "2025-02-10T07:46:20.951Z" }, + { url = "https://files.pythonhosted.org/packages/31/23/45feba8dae1d35fcca1e51b051f59dc4223cbd23e071a31e25f3f73938a8/lxml-5.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c", size = 5421580, upload-time = "2025-02-10T07:46:24.292Z" }, + { url = "https://files.pythonhosted.org/packages/61/69/be245d7b2dbef81c542af59c97fcd641fbf45accf2dc1c325bae7d0d014c/lxml-5.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f", size = 5285778, upload-time = "2025-02-10T07:46:28.801Z" }, + { url = "https://files.pythonhosted.org/packages/69/06/128af2ed04bac99b8f83becfb74c480f1aa18407b5c329fad457e08a1bf4/lxml-5.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645", size = 5054455, upload-time = "2025-02-10T07:46:31.665Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/f03a21cf6cc75cdd083563e509c7b6b159d761115c4142abb5481094ed8c/lxml-5.3.1-cp312-cp312-win32.whl", hash = "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5", size = 3486315, upload-time = "2025-02-10T07:46:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9c/8abe21585d20ef70ad9cec7562da4332b764ed69ec29b7389d23dfabcea0/lxml-5.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf", size = 3816925, upload-time = "2025-02-10T07:46:37.285Z" }, + { url = "https://files.pythonhosted.org/packages/94/1c/724931daa1ace168e0237b929e44062545bf1551974102a5762c349c668d/lxml-5.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e", size = 8171881, upload-time = "2025-02-10T07:46:40.653Z" }, + { url = "https://files.pythonhosted.org/packages/67/0c/857b8fb6010c4246e66abeebb8639eaabba60a6d9b7c606554ecc5cbf1ee/lxml-5.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd", size = 4440394, upload-time = "2025-02-10T07:46:44.037Z" }, + { url = "https://files.pythonhosted.org/packages/61/72/c9e81de6a000f9682ccdd13503db26e973b24c68ac45a7029173237e3eed/lxml-5.3.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7", size = 5037860, upload-time = "2025-02-10T07:46:47.919Z" }, + { url = "https://files.pythonhosted.org/packages/24/26/942048c4b14835711b583b48cd7209bd2b5f0b6939ceed2381a494138b14/lxml-5.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414", size = 4782513, upload-time = "2025-02-10T07:46:50.696Z" }, + { url = "https://files.pythonhosted.org/packages/e2/65/27792339caf00f610cc5be32b940ba1e3009b7054feb0c4527cebac228d4/lxml-5.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e", size = 5305227, upload-time = "2025-02-10T07:46:53.503Z" }, + { url = "https://files.pythonhosted.org/packages/18/e1/25f7aa434a4d0d8e8420580af05ea49c3e12db6d297cf5435ac0a054df56/lxml-5.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1", size = 4829846, upload-time = "2025-02-10T07:46:56.262Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ed/faf235e0792547d24f61ee1448159325448a7e4f2ab706503049d8e5df19/lxml-5.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5", size = 4949495, upload-time = "2025-02-10T07:46:59.189Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e1/8f572ad9ed6039ba30f26dd4c2c58fb90f79362d2ee35ca3820284767672/lxml-5.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423", size = 4773415, upload-time = "2025-02-10T07:47:03.53Z" }, + { url = "https://files.pythonhosted.org/packages/a3/75/6b57166b9d1983dac8f28f354e38bff8d6bcab013a241989c4d54c72701b/lxml-5.3.1-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20", size = 5337710, upload-time = "2025-02-10T07:47:06.385Z" }, + { url = "https://files.pythonhosted.org/packages/cc/71/4aa56e2daa83bbcc66ca27b5155be2f900d996f5d0c51078eaaac8df9547/lxml-5.3.1-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8", size = 4897362, upload-time = "2025-02-10T07:47:09.24Z" }, + { url = "https://files.pythonhosted.org/packages/65/10/3fa2da152cd9b49332fd23356ed7643c9b74cad636ddd5b2400a9730d12b/lxml-5.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9", size = 4977795, upload-time = "2025-02-10T07:47:12.101Z" }, + { url = "https://files.pythonhosted.org/packages/de/d2/e1da0f7b20827e7b0ce934963cb6334c1b02cf1bb4aecd218c4496880cb3/lxml-5.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c", size = 4858104, upload-time = "2025-02-10T07:47:15.998Z" }, + { url = "https://files.pythonhosted.org/packages/a5/35/063420e1b33d3308f5aa7fcbdd19ef6c036f741c9a7a4bd5dc8032486b27/lxml-5.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b", size = 5416531, upload-time = "2025-02-10T07:47:19.862Z" }, + { url = "https://files.pythonhosted.org/packages/c3/83/93a6457d291d1e37adfb54df23498101a4701834258c840381dd2f6a030e/lxml-5.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5", size = 5273040, upload-time = "2025-02-10T07:47:24.29Z" }, + { url = "https://files.pythonhosted.org/packages/39/25/ad4ac8fac488505a2702656550e63c2a8db3a4fd63db82a20dad5689cecb/lxml-5.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252", size = 5050951, upload-time = "2025-02-10T07:47:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/82/74/f7d223c704c87e44b3d27b5e0dde173a2fcf2e89c0524c8015c2b3554876/lxml-5.3.1-cp313-cp313-win32.whl", hash = "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78", size = 3485357, upload-time = "2025-02-10T07:47:29.738Z" }, + { url = "https://files.pythonhosted.org/packages/80/83/8c54533b3576f4391eebea88454738978669a6cad0d8e23266224007939d/lxml-5.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332", size = 3814484, upload-time = "2025-02-10T07:47:33.3Z" }, ] [[package]] name = "markdown" version = "3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086, upload-time = "2024-08-16T15:55:17.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349, upload-time = "2024-08-16T15:55:16.176Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "markupsafe" version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] [[package]] name = "mergedeep" version = "1.3.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, ] [[package]] @@ -349,9 +443,9 @@ dependencies = [ { name = "pyyaml-env-tag" }, { name = "verspec" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/f7/2933f1a1fb0e0f077d5d6a92c6c7f8a54e6128241f116dff4df8b6050bbf/mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810", size = 38119 } +sdist = { url = "https://files.pythonhosted.org/packages/ab/f7/2933f1a1fb0e0f077d5d6a92c6c7f8a54e6128241f116dff4df8b6050bbf/mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810", size = 38119, upload-time = "2024-08-13T05:02:14.167Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a", size = 33754 }, + { url = "https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a", size = 33754, upload-time = "2024-08-13T05:02:12.515Z" }, ] [[package]] @@ -373,9 +467,9 @@ dependencies = [ { name = "pyyaml-env-tag" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, ] [[package]] @@ -385,9 +479,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mkdocs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/88/1e/02ffe62a3c463973db22dbef1c04c8d5e409f4ea5887d158c6aa81770bec/mkdocs_asciinema_player-0.16.0.tar.gz", hash = "sha256:4c30fca7351decdb69eff5176d58097cf593f3e8a886e992899c9f2910ce5450", size = 89930 } +sdist = { url = "https://files.pythonhosted.org/packages/88/1e/02ffe62a3c463973db22dbef1c04c8d5e409f4ea5887d158c6aa81770bec/mkdocs_asciinema_player-0.16.0.tar.gz", hash = "sha256:4c30fca7351decdb69eff5176d58097cf593f3e8a886e992899c9f2910ce5450", size = 89930, upload-time = "2025-02-12T10:32:51.577Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/3d/9368776e95d42aff43acca746ca8e37f7abe2e2747c934dfe5cc55f23eab/mkdocs_asciinema_player-0.16.0-py3-none-any.whl", hash = "sha256:09a87fa1b1390b25bf38b8b8528f93f1cfd4ae181598231300a58e4665196b11", size = 92556 }, + { url = "https://files.pythonhosted.org/packages/cb/3d/9368776e95d42aff43acca746ca8e37f7abe2e2747c934dfe5cc55f23eab/mkdocs_asciinema_player-0.16.0-py3-none-any.whl", hash = "sha256:09a87fa1b1390b25bf38b8b8528f93f1cfd4ae181598231300a58e4665196b11", size = 92556, upload-time = "2025-02-12T10:32:47.88Z" }, ] [[package]] @@ -399,9 +493,9 @@ dependencies = [ { name = "platformdirs" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, ] [[package]] @@ -414,18 +508,18 @@ dependencies = [ { name = "mkdocs" }, { name = "pytz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/73/85/6dc9d4eca486ed5734a05f7fd5c612a8e60a35e65610dad6aa9c58118c3f/mkdocs_git_revision_date_localized_plugin-1.3.0.tar.gz", hash = "sha256:439e2f14582204050a664c258861c325064d97cdc848c541e48bb034a6c4d0cb", size = 384797 } +sdist = { url = "https://files.pythonhosted.org/packages/73/85/6dc9d4eca486ed5734a05f7fd5c612a8e60a35e65610dad6aa9c58118c3f/mkdocs_git_revision_date_localized_plugin-1.3.0.tar.gz", hash = "sha256:439e2f14582204050a664c258861c325064d97cdc848c541e48bb034a6c4d0cb", size = 384797, upload-time = "2024-10-22T12:45:13.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/e5/ffeb92db53af8c3aa2d92e21a3cf6b5f83eee7e03b9cf9234ef6b30230d5/mkdocs_git_revision_date_localized_plugin-1.3.0-py3-none-any.whl", hash = "sha256:c99377ee119372d57a9e47cff4e68f04cce634a74831c06bc89b33e456e840a1", size = 22549 }, + { url = "https://files.pythonhosted.org/packages/67/e5/ffeb92db53af8c3aa2d92e21a3cf6b5f83eee7e03b9cf9234ef6b30230d5/mkdocs_git_revision_date_localized_plugin-1.3.0-py3-none-any.whl", hash = "sha256:c99377ee119372d57a9e47cff4e68f04cce634a74831c06bc89b33e456e840a1", size = 22549, upload-time = "2024-10-22T12:45:11.339Z" }, ] [[package]] name = "mkdocs-glightbox" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/5a/0bc456397ba0acc684b5b1daa4ca232ed717938fd37198251d8bcc4053bf/mkdocs-glightbox-0.4.0.tar.gz", hash = "sha256:392b34207bf95991071a16d5f8916d1d2f2cd5d5bb59ae2997485ccd778c70d9", size = 32010 } +sdist = { url = "https://files.pythonhosted.org/packages/86/5a/0bc456397ba0acc684b5b1daa4ca232ed717938fd37198251d8bcc4053bf/mkdocs-glightbox-0.4.0.tar.gz", hash = "sha256:392b34207bf95991071a16d5f8916d1d2f2cd5d5bb59ae2997485ccd778c70d9", size = 32010, upload-time = "2024-05-06T14:31:43.063Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/72/b0c2128bb569c732c11ae8e49a777089e77d83c05946062caa19b841e6fb/mkdocs_glightbox-0.4.0-py3-none-any.whl", hash = "sha256:e0107beee75d3eb7380ac06ea2d6eac94c999eaa49f8c3cbab0e7be2ac006ccf", size = 31154 }, + { url = "https://files.pythonhosted.org/packages/c1/72/b0c2128bb569c732c11ae8e49a777089e77d83c05946062caa19b841e6fb/mkdocs_glightbox-0.4.0-py3-none-any.whl", hash = "sha256:e0107beee75d3eb7380ac06ea2d6eac94c999eaa49f8c3cbab0e7be2ac006ccf", size = 31154, upload-time = "2024-05-06T14:31:41.011Z" }, ] [[package]] @@ -435,9 +529,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mkdocs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/f9/c48a04f3cf484f8016a343c1d7d99c3a1ef01dbb33ceabb1d02e0ecabda7/mkdocs_literate_nav-0.6.1.tar.gz", hash = "sha256:78a7ab6d878371728acb0cdc6235c9b0ffc6e83c997b037f4a5c6ff7cef7d759", size = 16437 } +sdist = { url = "https://files.pythonhosted.org/packages/4d/f9/c48a04f3cf484f8016a343c1d7d99c3a1ef01dbb33ceabb1d02e0ecabda7/mkdocs_literate_nav-0.6.1.tar.gz", hash = "sha256:78a7ab6d878371728acb0cdc6235c9b0ffc6e83c997b037f4a5c6ff7cef7d759", size = 16437, upload-time = "2023-09-10T22:17:16.815Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/3b/e00d839d3242844c77e248f9572dd34644a04300839a60fe7d6bf652ab19/mkdocs_literate_nav-0.6.1-py3-none-any.whl", hash = "sha256:e70bdc4a07050d32da79c0b697bd88e9a104cf3294282e9cb20eec94c6b0f401", size = 13182 }, + { url = "https://files.pythonhosted.org/packages/51/3b/e00d839d3242844c77e248f9572dd34644a04300839a60fe7d6bf652ab19/mkdocs_literate_nav-0.6.1-py3-none-any.whl", hash = "sha256:e70bdc4a07050d32da79c0b697bd88e9a104cf3294282e9cb20eec94c6b0f401", size = 13182, upload-time = "2023-09-10T22:17:18.751Z" }, ] [[package]] @@ -448,9 +542,9 @@ dependencies = [ { name = "mkdocs" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/c4/142510a1ea41465f63dc0419748ce693d96d714f4fe06d086fcd11b4435b/mkdocs_markdownextradata_plugin-0.2.6.tar.gz", hash = "sha256:4aed9b43b8bec65b02598387426ca4809099ea5f5aa78bf114f3296fd46686b5", size = 6882 } +sdist = { url = "https://files.pythonhosted.org/packages/19/c4/142510a1ea41465f63dc0419748ce693d96d714f4fe06d086fcd11b4435b/mkdocs_markdownextradata_plugin-0.2.6.tar.gz", hash = "sha256:4aed9b43b8bec65b02598387426ca4809099ea5f5aa78bf114f3296fd46686b5", size = 6882, upload-time = "2024-08-22T08:57:10.227Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl", hash = "sha256:34dd40870781784c75809596b2d8d879da783815b075336d541de1f150c94242", size = 6665 }, + { url = "https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl", hash = "sha256:34dd40870781784c75809596b2d8d879da783815b075336d541de1f150c94242", size = 6665, upload-time = "2024-08-22T08:57:07.75Z" }, ] [[package]] @@ -470,18 +564,31 @@ dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/4d/0a9f6f604f01eaa43df3b3b30b5218548efd7341913b302815585f48abb2/mkdocs_material-9.6.5.tar.gz", hash = "sha256:b714679a8c91b0ffe2188e11ed58c44d2523e9c2ae26a29cc652fa7478faa21f", size = 3946479 } +sdist = { url = "https://files.pythonhosted.org/packages/38/4d/0a9f6f604f01eaa43df3b3b30b5218548efd7341913b302815585f48abb2/mkdocs_material-9.6.5.tar.gz", hash = "sha256:b714679a8c91b0ffe2188e11ed58c44d2523e9c2ae26a29cc652fa7478faa21f", size = 3946479, upload-time = "2025-02-20T01:27:42.943Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/05/7d440b23454c0fc8cdba21f73ce23369eb16e7f7ee475fac3a4ad15ad5e0/mkdocs_material-9.6.5-py3-none-any.whl", hash = "sha256:aad3e6fb860c20870f75fb2a69ef901f1be727891e41adb60b753efcae19453b", size = 8695060 }, + { url = "https://files.pythonhosted.org/packages/3d/05/7d440b23454c0fc8cdba21f73ce23369eb16e7f7ee475fac3a4ad15ad5e0/mkdocs_material-9.6.5-py3-none-any.whl", hash = "sha256:aad3e6fb860c20870f75fb2a69ef901f1be727891e41adb60b753efcae19453b", size = 8695060, upload-time = "2025-02-20T01:27:37.963Z" }, ] [[package]] name = "mkdocs-material-extensions" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, +] + +[[package]] +name = "mkdocs-redirects" +version = "1.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, + { name = "properdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/25/49725f78ca5d3026b09973f7a2b3a8b179cc2e8c15e43d5a13bc79f6b274/mkdocs_redirects-1.2.3.tar.gz", hash = "sha256:5e980330999299729a2d6a125347d1af78023d68a23681a4de3053ce7dfe2e51", size = 7712, upload-time = "2026-03-28T13:57:41.766Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, + { url = "https://files.pythonhosted.org/packages/c6/90/871b1cddc01d2ba1637b858eeeabc2e3013dc8df591306b5567b98ef0870/mkdocs_redirects-1.2.3-py3-none-any.whl", hash = "sha256:ec7312fff462d03ec16395d0c001006a418f8d0c21cdf2b47ff11cf839dc3ce0", size = 6245, upload-time = "2026-03-28T13:57:40.466Z" }, ] [[package]] @@ -492,101 +599,141 @@ dependencies = [ { name = "lxml" }, { name = "mkdocs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/16/45213649b6756744f36f31014fc8673df1d7c998bb9a801c2d769fff4114/mkdocs-video-1.5.0.tar.gz", hash = "sha256:0defc018f4b7927f8afffc4d8e039c84dfba636dffc5e25e2bfa8d6350bc8eca", size = 5633 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/16/45213649b6756744f36f31014fc8673df1d7c998bb9a801c2d769fff4114/mkdocs-video-1.5.0.tar.gz", hash = "sha256:0defc018f4b7927f8afffc4d8e039c84dfba636dffc5e25e2bfa8d6350bc8eca", size = 5633, upload-time = "2023-03-17T17:36:43.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/3a/b51e305eca21cdd58f4eb06973099ba0ea679b1f437a92a49f8fc576310e/mkdocs_video-1.5.0-py3-none-any.whl", hash = "sha256:b35613d4dacbac2dfa94d8c2600383cda14ad99a1fa1542b5fc4e9c6d19e9fe1", size = 6570 }, + { url = "https://files.pythonhosted.org/packages/cc/3a/b51e305eca21cdd58f4eb06973099ba0ea679b1f437a92a49f8fc576310e/mkdocs_video-1.5.0-py3-none-any.whl", hash = "sha256:b35613d4dacbac2dfa94d8c2600383cda14ad99a1fa1542b5fc4e9c6d19e9fe1", size = 6570, upload-time = "2023-03-17T17:36:40.776Z" }, +] + +[[package]] +name = "neoteroi-mkdocs" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "essentials-openapi" }, + { name = "httpx" }, + { name = "jinja2" }, + { name = "mkdocs" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/36/7cc711922112a1293568b8bc2f94bfaae0104bf5c1e5b3cdcfd6068023c7/neoteroi_mkdocs-1.2.0.tar.gz", hash = "sha256:58e25cb1b9db093ffa8d12bdb33264bf567cac30fb964b56e0a493efa749ad6e", size = 25354, upload-time = "2025-11-23T10:49:23.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/e9/d365600c11aa84142b878e6655d23c0add7a056d3d6e28ff8f83d7dd2381/neoteroi_mkdocs-1.2.0-py3-none-any.whl", hash = "sha256:91b6aa95a4e46c9bb93e00e021d2044cb0c7d80c0b4600434ff8f440d613a0a8", size = 38485, upload-time = "2025-11-23T10:49:22.149Z" }, ] [[package]] name = "packaging" version = "24.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, ] [[package]] name = "paginate" version = "0.5.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] [[package]] name = "pillow" version = "11.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 }, - { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 }, - { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 }, - { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 }, - { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 }, - { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 }, - { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 }, - { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 }, - { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 }, - { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 }, - { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 }, - { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, - { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, - { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, - { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, - { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, - { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, - { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, - { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, - { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, - { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, - { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, - { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, - { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, - { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, - { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, - { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, - { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, - { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, - { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715, upload-time = "2025-01-02T08:13:58.407Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818, upload-time = "2025-01-02T08:11:22.518Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662, upload-time = "2025-01-02T08:11:25.19Z" }, + { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317, upload-time = "2025-01-02T08:11:30.371Z" }, + { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999, upload-time = "2025-01-02T08:11:33.499Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819, upload-time = "2025-01-02T08:11:37.304Z" }, + { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081, upload-time = "2025-01-02T08:11:39.598Z" }, + { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513, upload-time = "2025-01-02T08:11:43.083Z" }, + { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298, upload-time = "2025-01-02T08:11:46.626Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630, upload-time = "2025-01-02T08:11:49.401Z" }, + { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369, upload-time = "2025-01-02T08:11:52.02Z" }, + { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240, upload-time = "2025-01-02T08:11:56.193Z" }, + { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640, upload-time = "2025-01-02T08:11:58.329Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437, upload-time = "2025-01-02T08:12:01.797Z" }, + { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605, upload-time = "2025-01-02T08:12:05.224Z" }, + { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173, upload-time = "2025-01-02T08:12:08.281Z" }, + { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145, upload-time = "2025-01-02T08:12:11.411Z" }, + { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340, upload-time = "2025-01-02T08:12:15.29Z" }, + { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906, upload-time = "2025-01-02T08:12:17.485Z" }, + { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759, upload-time = "2025-01-02T08:12:20.382Z" }, + { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657, upload-time = "2025-01-02T08:12:23.922Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304, upload-time = "2025-01-02T08:12:28.069Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117, upload-time = "2025-01-02T08:12:30.064Z" }, + { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060, upload-time = "2025-01-02T08:12:32.362Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192, upload-time = "2025-01-02T08:12:34.361Z" }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805, upload-time = "2025-01-02T08:12:36.99Z" }, + { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623, upload-time = "2025-01-02T08:12:41.912Z" }, + { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191, upload-time = "2025-01-02T08:12:45.186Z" }, + { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494, upload-time = "2025-01-02T08:12:47.098Z" }, + { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595, upload-time = "2025-01-02T08:12:50.47Z" }, + { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651, upload-time = "2025-01-02T08:12:53.356Z" }, ] [[package]] name = "platformdirs" version = "4.3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302, upload-time = "2024-09-17T19:06:50.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439, upload-time = "2024-09-17T19:06:49.212Z" }, +] + +[[package]] +name = "properdocs" +version = "1.6.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/29/f27a4e1eddf72ed3db6e47818fbafe6debbf09fd7051f9c1a007239b46ef/properdocs-1.6.7.tar.gz", hash = "sha256:adc7b16e562890af0e098a7e5b02e3a81c20894a87d6a28d345c9300de73c26e", size = 276141, upload-time = "2026-03-20T20:07:48.167Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, + { url = "https://files.pythonhosted.org/packages/bd/4d/fc923f5c85318ee8cc903566dc4e0ebe41b2dfc1d2ecf5546db232397ed6/properdocs-1.6.7-py3-none-any.whl", hash = "sha256:6fa0cfa2e01bf338f684892c8a506cf70ea88ae7f3479c933b6fa20168101cbd", size = 225406, upload-time = "2026-03-20T20:07:46.875Z" }, ] [[package]] name = "pycparser" version = "2.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, ] [[package]] name = "pygments" version = "2.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, ] [[package]] @@ -597,18 +744,18 @@ dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/44/e6de2fdc880ad0ec7547ca2e087212be815efbc9a425a8d5ba9ede602cbb/pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b", size = 846846 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/44/e6de2fdc880ad0ec7547ca2e087212be815efbc9a425a8d5ba9ede602cbb/pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b", size = 846846, upload-time = "2025-02-01T15:43:15.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/f5/b9e2a42aa8f9e34d52d66de87941ecd236570c7ed2e87775ed23bbe4e224/pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", size = 264467 }, + { url = "https://files.pythonhosted.org/packages/eb/f5/b9e2a42aa8f9e34d52d66de87941ecd236570c7ed2e87775ed23bbe4e224/pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", size = 264467, upload-time = "2025-02-01T15:43:13.995Z" }, ] [[package]] name = "pyparsing" version = "3.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 } +sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694, upload-time = "2024-12-31T20:59:46.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 }, + { url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716, upload-time = "2024-12-31T20:59:42.738Z" }, ] [[package]] @@ -618,44 +765,44 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] [[package]] name = "pytz" version = "2025.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 } +sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617, upload-time = "2025-01-31T01:54:48.615Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, + { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930, upload-time = "2025-01-31T01:54:45.634Z" }, ] [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] [[package]] @@ -665,47 +812,47 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631, upload-time = "2020-11-12T02:38:26.239Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, + { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911, upload-time = "2020-11-12T02:38:24.638Z" }, ] [[package]] name = "regex" version = "2024.11.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, - { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, - { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, - { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, - { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, - { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, - { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, - { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, - { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, - { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, - { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, - { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, - { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, - { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, - { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, - { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, - { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, - { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, - { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, - { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, - { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, - { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, - { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, - { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, - { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, - { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, - { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, - { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, - { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, - { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781, upload-time = "2024-11-06T20:10:07.07Z" }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455, upload-time = "2024-11-06T20:10:09.117Z" }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759, upload-time = "2024-11-06T20:10:11.155Z" }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976, upload-time = "2024-11-06T20:10:13.24Z" }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077, upload-time = "2024-11-06T20:10:15.37Z" }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160, upload-time = "2024-11-06T20:10:19.027Z" }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896, upload-time = "2024-11-06T20:10:21.85Z" }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997, upload-time = "2024-11-06T20:10:24.329Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725, upload-time = "2024-11-06T20:10:28.067Z" }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481, upload-time = "2024-11-06T20:10:31.612Z" }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896, upload-time = "2024-11-06T20:10:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138, upload-time = "2024-11-06T20:10:36.142Z" }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692, upload-time = "2024-11-06T20:10:38.394Z" }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135, upload-time = "2024-11-06T20:10:40.367Z" }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567, upload-time = "2024-11-06T20:10:43.467Z" }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525, upload-time = "2024-11-06T20:10:45.19Z" }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324, upload-time = "2024-11-06T20:10:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617, upload-time = "2024-11-06T20:10:49.312Z" }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023, upload-time = "2024-11-06T20:10:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072, upload-time = "2024-11-06T20:10:52.926Z" }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130, upload-time = "2024-11-06T20:10:54.828Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857, upload-time = "2024-11-06T20:10:56.634Z" }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006, upload-time = "2024-11-06T20:10:59.369Z" }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650, upload-time = "2024-11-06T20:11:02.042Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545, upload-time = "2024-11-06T20:11:03.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045, upload-time = "2024-11-06T20:11:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182, upload-time = "2024-11-06T20:11:09.06Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733, upload-time = "2024-11-06T20:11:11.256Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122, upload-time = "2024-11-06T20:11:13.161Z" }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545, upload-time = "2024-11-06T20:11:15Z" }, ] [[package]] @@ -718,27 +865,79 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "romm-docs" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "cairosvg" }, + { name = "mike" }, + { name = "mkdocs" }, + { name = "mkdocs-asciinema-player" }, + { name = "mkdocs-git-revision-date-localized-plugin" }, + { name = "mkdocs-glightbox" }, + { name = "mkdocs-literate-nav" }, + { name = "mkdocs-markdownextradata-plugin" }, + { name = "mkdocs-material" }, + { name = "mkdocs-redirects" }, + { name = "mkdocs-video" }, + { name = "neoteroi-mkdocs" }, + { name = "pillow" }, + { name = "tomli" }, +] + +[package.metadata] +requires-dist = [ + { name = "cairosvg", specifier = ">=2.7.1" }, + { name = "mike", specifier = ">=2.1.3" }, + { name = "mkdocs", specifier = ">=1.6.1" }, + { name = "mkdocs-asciinema-player", specifier = ">=0.16.0" }, + { name = "mkdocs-git-revision-date-localized-plugin", specifier = ">=1.3.0" }, + { name = "mkdocs-glightbox", specifier = ">=0.4.0" }, + { name = "mkdocs-literate-nav", specifier = ">=0.6.1" }, + { name = "mkdocs-markdownextradata-plugin", specifier = ">=0.2.6" }, + { name = "mkdocs-material", specifier = ">=9.6.5" }, + { name = "mkdocs-redirects", specifier = ">=1.2.2" }, + { name = "mkdocs-video", specifier = ">=1.5.0" }, + { name = "neoteroi-mkdocs", specifier = ">=1.1.0" }, + { name = "pillow", specifier = ">=11.1.0" }, + { name = "tomli", specifier = ">=2.0.1" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "smmap" version = "5.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" }, ] [[package]] @@ -748,100 +947,121 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "webencodings" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "urllib3" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" }, ] [[package]] name = "verspec" version = "0.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/44/8126f9f0c44319b2efc65feaad589cadef4d77ece200ae3c9133d58464d0/verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e", size = 27123 } +sdist = { url = "https://files.pythonhosted.org/packages/e7/44/8126f9f0c44319b2efc65feaad589cadef4d77ece200ae3c9133d58464d0/verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e", size = 27123, upload-time = "2020-11-30T02:24:09.646Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31", size = 19640 }, + { url = "https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31", size = 19640, upload-time = "2020-11-30T02:24:08.387Z" }, ] [[package]] name = "watchdog" version = "6.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] [[package]] name = "webencodings" version = "0.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, -] - -[[package]] -name = "romm-docs" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "cairosvg" }, - { name = "mike" }, - { name = "mkdocs" }, - { name = "mkdocs-asciinema-player" }, - { name = "mkdocs-git-revision-date-localized-plugin" }, - { name = "mkdocs-glightbox" }, - { name = "mkdocs-literate-nav" }, - { name = "mkdocs-markdownextradata-plugin" }, - { name = "mkdocs-material" }, - { name = "mkdocs-video" }, - { name = "pillow" }, -] - -[package.metadata] -requires-dist = [ - { name = "cairosvg", specifier = ">=2.7.1" }, - { name = "mike", specifier = ">=2.1.3" }, - { name = "mkdocs", specifier = ">=1.6.1" }, - { name = "mkdocs-asciinema-player", specifier = ">=0.16.0" }, - { name = "mkdocs-git-revision-date-localized-plugin", specifier = ">=1.3.0" }, - { name = "mkdocs-glightbox", specifier = ">=0.4.0" }, - { name = "mkdocs-literate-nav", specifier = ">=0.6.1" }, - { name = "mkdocs-markdownextradata-plugin", specifier = ">=0.2.6" }, - { name = "mkdocs-material", specifier = ">=9.6.5" }, - { name = "mkdocs-video", specifier = ">=1.5.0" }, - { name = "pillow", specifier = ">=11.1.0" }, + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, ] [[package]] name = "zipp" version = "3.21.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545, upload-time = "2024-11-10T15:05:20.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630, upload-time = "2024-11-10T15:05:19.275Z" }, ] From f65b062cce8573404343fb11dc060698455b85d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 16:15:48 +0000 Subject: [PATCH 002/121] docs: draft Wave 1 anchor pages (landing + quick-start + install/admin/dev) First batch of real Wave 1 content for the v5.0 overhaul. These pages establish the tone and cross-linking pattern for the rest of the wave. - index.md: routing landing with intent-based cards - getting-started/quick-start.md: 15-min docker-compose happy path, reusing the existing compose snippet and asciinema cast - install/docker-compose.md: canonical production reference with volume table, env var table, and hardening checklist - install/image-variants.md: slim vs full matrix + what's actually different between them - install/reverse-proxy.md: Caddy / nginx / Traefik / NPM recipes with ROMM_BASE_URL guidance - administration/users-and-roles.md: full 19-scope matrix across Viewer/Editor/Admin, invite flow, OIDC hooks - developers/api-authentication.md: session / Basic / OAuth2 / Client API Token / OIDC flows with error semantics Builds clean with --strict. --- docs/administration/users-and-roles.md | 80 +++++++++- docs/developers/api-authentication.md | 150 ++++++++++++++++++- docs/getting-started/quick-start.md | 108 +++++++++++++- docs/index.md | 70 +++++++-- docs/install/docker-compose.md | 85 ++++++++++- docs/install/image-variants.md | 44 +++++- docs/install/reverse-proxy.md | 193 ++++++++++++++++++++++++- 7 files changed, 685 insertions(+), 45 deletions(-) diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index 1f91d251..7157dea2 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -1,10 +1,80 @@ --- -status: placeholder -wave: 1 +title: Users & Roles +description: User management, roles, and the scope model in RomM 5.0. --- # Users & Roles -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM is multi-user from the start. The first user created during Setup is always an **Admin**; everyone after that gets the role you assign when creating the account. + +## Roles + +| Role | Who it's for | Default scopes | +| --- | --- | --- | +| **Admin** | You, and anyone you fully trust. | All scopes, including user management and task execution. | +| **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | +| **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | + +Roles are a convenience layer on top of **scopes** — see the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0; if you need finer-grained access, use the role that's most restrictive and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. + +## Scope matrix + +RomM authorisation is scope-based. Every API call and UI action maps to one or more scopes; OAuth tokens and OIDC sessions carry a subset of them. Nineteen scopes total, grouped by resource: + +| Scope | Purpose | Viewer | Editor | Admin | +| --- | --- | :---: | :---: | :---: | +| `me.read` | View own profile | ✓ | ✓ | ✓ | +| `me.write` | Edit own profile | ✓ | ✓ | ✓ | +| `roms.read` | Browse ROMs | ✓ | ✓ | ✓ | +| `roms.user.read` | View own per-ROM data (rating, playtime, notes) | ✓ | ✓ | ✓ | +| `roms.user.write` | Edit own per-ROM data | ✓ | ✓ | ✓ | +| `platforms.read` | Browse platforms | ✓ | ✓ | ✓ | +| `assets.read` | View own saves/states/screenshots | ✓ | ✓ | ✓ | +| `assets.write` | Upload saves/states/screenshots | ✓ | ✓ | ✓ | +| `collections.read` | Browse collections | ✓ | ✓ | ✓ | +| `collections.write` | Create/edit collections | — | ✓ | ✓ | +| `roms.write` | Edit ROM metadata | — | ✓ | ✓ | +| `platforms.write` | Edit/create platforms | — | ✓ | ✓ | +| `firmware.read` | List firmware | — | ✓ | ✓ | +| `firmware.write` | Upload/delete firmware | — | ✓ | ✓ | +| `devices.read` | View own paired devices | ✓ | ✓ | ✓ | +| `devices.write` | Manage own paired devices | ✓ | ✓ | ✓ | +| `users.read` | List all users | — | — | ✓ | +| `users.write` | Create/edit/delete users | — | — | ✓ | +| `tasks.run` | Trigger background tasks (scan, cleanup, etc.) | — | — | ✓ | + +## Creating users + +Two ways: + +### Admin adds directly + +**Administration → Users → Add.** Set username, email, password, and role. The account is usable immediately. + +### Invite link + +Better when you don't want to handle someone else's password. + +1. **Administration → Users → Invite.** Pick a role; RomM generates a single-use invite link. +2. Send the link. The recipient opens it, picks their own username and password, and is logged in. +3. Invite links expire — the default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). + +### Public self-registration + +Off by default. To let anyone with the URL register their own Viewer account, set `ALLOW_PUBLIC_REGISTRATION=true`. Only enable this if your instance is behind auth at the reverse-proxy layer (Authelia, etc.) or you genuinely want open registration — once on, anyone who reaches `/register` can create an account. + +### OIDC + +If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md) — look for `OIDC_CLAIM_ROLES` and the per-role env vars. + +## Editing and deleting users + +- **Change role**: Admin → Users → Edit → Role dropdown. Takes effect on next login. +- **Reset password**: Admin → Users → Edit → New password. For self-service, the user can use the "Forgot password" flow from the login page if email is configured. +- **Delete**: Admin → Users → red delete icon → confirm. RomM won't let you delete the last admin or delete yourself while signed in. + +Deleting a user keeps their contributions (collections they made public, ROM metadata edits) but removes their personal data (per-ROM ratings, saves, states, play sessions, paired devices, API tokens). + +## API tokens (advanced) + +Each user can issue up to 25 **Client API Tokens** from **Administration → Client API Tokens**. Tokens carry a subset of the user's scopes and are the right way to authenticate companion apps (Argosy, Grout, Playnite, custom scripts). The pairing flow for devices is covered in [Client API Tokens](../ecosystem/client-api-tokens.md); the API side is in [API Authentication](../developers/api-authentication.md). diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index 6b219733..42702dc7 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -1,10 +1,150 @@ --- -status: placeholder -wave: 1 +title: API Authentication +description: How to authenticate to the RomM REST API — session cookies, Basic, OAuth2 tokens, client tokens, and OIDC. --- # API Authentication -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM's REST API accepts four authentication modes. Pick the one that matches your client: + +| Mode | Who it's for | How the credential is carried | +| --- | --- | --- | +| **Session cookie** | Browser UI | `Cookie: session=…` after `POST /api/auth/login` | +| **HTTP Basic** | Quick scripts, curl one-liners | `Authorization: Basic ` | +| **OAuth2 Bearer** | Automation, CI, third-party apps | `Authorization: Bearer ` | +| **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | +| **OIDC session** | Users who sign in via SSO | Same as session cookie, but issued after OIDC callback | + +All of them resolve to the same scope model — see the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. + +## Base URL + +```text +https:///api +``` + +When RomM is behind a reverse proxy (as it should be in production), that's your public URL. When running locally without a proxy, the container listens on port `8080`. + +## Session login (browsers) + +```http +POST /api/auth/login +Content-Type: application/x-www-form-urlencoded + +username=alice&password=s3cret +``` + +Response sets a `session` cookie. Subsequent requests from the same browser are authenticated automatically. + +Log out: + +```http +POST /api/auth/logout +``` + +For OIDC logins, hitting `/api/auth/logout` also triggers RP-Initiated Logout if your OIDC provider supports it (configured via `OIDC_END_SESSION_ENDPOINT`). + +## HTTP Basic + +Fine for quick scripts. Avoid in shared environments — the credentials are sent on every request. + +```bash +curl -u alice:s3cret https://romm.example.com/api/roms +``` + +```python +import requests +from requests.auth import HTTPBasicAuth +r = requests.get("https://romm.example.com/api/roms", + auth=HTTPBasicAuth("alice", "s3cret")) +``` + +## OAuth2 Bearer token + +The RomM backend implements the OAuth2 password grant. Exchange credentials for a short-lived access token and a refresh token: + +```http +POST /api/token +Content-Type: application/x-www-form-urlencoded + +grant_type=password&username=alice&password=s3cret&scope=roms.read%20roms.write +``` + +```json +{ + "access_token": "eyJhbGciOi...", + "token_type": "bearer", + "expires_in": 900, + "refresh_token": "eyJhbGciOi..." +} +``` + +Access tokens are HS256-signed JWTs valid for ~15 minutes. Send them as: + +``` +Authorization: Bearer eyJhbGciOi... +``` + +Refresh before expiry: + +```http +POST /api/token +Content-Type: application/x-www-form-urlencoded + +grant_type=refresh_token&refresh_token=eyJhbGciOi... +``` + +Request only the scopes you need — RomM will issue a token with the intersection of what you asked for and what the user has. + +## Client API tokens (for companion apps) + +For anything long-lived — a running companion app, a cron job, a CI integration — use **Client API Tokens** instead of OAuth2. They're issued per-user from **Administration → Client API Tokens**, carry a subset of the user's scopes, and don't expire unless you set an expiry. + +Token format: `rmm_` + 40 hex chars. Use it as a bearer: + +```bash +curl -H "Authorization: Bearer rmm_abcdef0123456789..." \ + https://romm.example.com/api/roms +``` + +Each user gets up to 25 active tokens. Tokens can be paired with a device via the [pairing flow](../ecosystem/client-api-tokens.md) — useful when you don't want to type a long token on a handheld. + +## OIDC + +Users signing in through an OIDC provider get a regular RomM session, same as username/password login. For the API side this means you can't use an OIDC access token directly — authenticate the user through the browser first (they'll be redirected to the OIDC provider, then back to RomM), then use the resulting session cookie, **or** mint a Client API Token for programmatic use. + +OIDC provider setup lives in [Administration → OIDC](../administration/oidc/index.md). + +## Which scopes do I need? + +Every endpoint in the [API Reference](api-reference.md) lists its required scopes. The short version: + +- **Read**-ish endpoints want the matching `*.read` scope. +- **Write**-ish endpoints want `*.write`. +- **Admin** endpoints (anything under `/api/users` beyond `me`, `/api/tasks/run`) want `users.read`, `users.write`, or `tasks.run`. + +A token that holds `users.write` also implicitly grants lesser scopes like `users.read` or `me.read` — RomM doesn't require you to list them all. But a token that holds only `roms.read` can't write, even if the underlying user is an Admin. + +## Errors + +| HTTP | Meaning | +| --- | --- | +| `401 Unauthorized` | No credential, expired credential, bad credential. | +| `403 Forbidden` | Authenticated, but the identity lacks a required scope. | +| `404 Not Found` | The resource doesn't exist — or, for privacy, the identity can't see it. | + +When debugging a 403, check: + +1. The **user's role** in Administration → Users. +2. The **token's scopes** (for OAuth2/Client API Tokens) — scopes are narrower than the user's role by default. +3. The endpoint's scope requirements in the [API Reference](api-reference.md). + +## OpenAPI + +The full machine-readable schema is served at `/openapi.json`. It's the source of truth for generated clients, Postman collections, and the in-docs [API Reference](api-reference.md). + +```bash +curl https://romm.example.com/openapi.json > romm-openapi.json +``` + +See [Consuming OpenAPI](openapi.md) for codegen tips. diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index 80fe69ca..9e5c1ac0 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -1,10 +1,108 @@ --- -status: placeholder -wave: 1 +title: Quick Start +description: Get a RomM 5.0 instance running in about fifteen minutes using Docker Compose. --- # Quick Start -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +This guide gets a RomM instance up and running with the default stack (MariaDB + Redis + RomM) using Docker Compose. If you're on Unraid, Synology, TrueNAS, or Kubernetes, start there instead: the [Install & Deploy](../install/index.md) section has platform-specific guides. + +## Before you start + +You'll need: + +- [Docker](https://docs.docker.com/get-docker/) and Docker Compose installed on the host. +- Your ROM files organised in the expected [folder structure](folder-structure.md). +- API credentials for at least one [metadata provider](../administration/metadata-providers.md). IGDB + ScreenScraper is the recommended pairing; RomM will run without any provider configured, but matching quality will suffer. + +!!! warning "Metadata providers are recommended" + RomM works without a metadata API for basic use, but setup problems and companion-app integrations (e.g. Playnite) can fail without them. Setting up **IGDB** API keys before your first scan is strongly recommended. + +## 1. Write your `docker-compose.yml` + +Start from the reference file shipped in the RomM repo — a known-good, minimally-edited version is included below. Save it as `docker-compose.yml` in an empty directory on your host. + +???+ example "docker-compose.yml" + ``` yaml + --8<-- "quick-start.docker-compose.yml" + ``` + +You'll want to edit the following values before launching: + +| Where | Variable | What to put | +| --- | --- | --- | +| `romm-db` service | `MARIADB_ROOT_PASSWORD` | A long random password. Generate one — don't reuse. | +| `romm-db` service | `MARIADB_PASSWORD` | A separate long random password for the `romm-user`. | +| `romm` service | `DB_PASSWD` | Must match `MARIADB_PASSWORD` above. | +| `romm` service | `ROMM_AUTH_SECRET_KEY` | Generate with `openssl rand -hex 32`. Keep it secret. | +| `romm` service | Metadata provider creds | Fill in only the providers you've registered with. See [Metadata Providers](../administration/metadata-providers.md). | +| `romm` service | `/path/to/library` volume | Absolute path to the directory containing your `roms/` folder. | +| `romm` service | `/path/to/assets` volume | Absolute path where RomM will store saves, states, uploaded screenshots. | +| `romm` service | `/path/to/config` volume | Absolute path to a directory that will hold `config.yml`. | + +Generate the auth secret now so you don't forget: + +```sh +openssl rand -hex 32 +# -> 03a054b6ca27e0107c5eed552ea66becd9f3a2a8a91e7595cd462a593f9ecd09 +``` + +## 2. Start the stack + +From the directory containing your `docker-compose.yml`: + +```sh +docker compose up -d +``` + +On the first run Docker will pull `rommapp/romm:latest` and `mariadb:latest`, bring up the database, wait for the healthcheck, then start RomM. Verify everything is running: + +```sh +docker ps -f name=romm +``` + +```asciinema-player + { + "file": "../resources/asciinema/quick-start-docker-compose.cast", + "title": "RomM docker compose install", + "preload": true, + "loop": true, + "auto_play": true, + "cols": 140, + "rows": 30, + "fit": "width", + "terminal_font_size": "small", + "terminal_line_height": "1.2", + "terminal_font_family": "Roboto Mono, Monaco, Consolas, monospace" + } +``` + +## 3. Create the admin user + +Open `http://:80` in a browser. The first time RomM starts, you'll be redirected to the **Setup Wizard**. Set an admin username and password — the first user created always gets the Admin role — then log in. + +## 4. Scan your library + +The fastest way to populate RomM is to let it scan your mounted library: + +1. Click **Scan** in the sidebar. +2. Pick the metadata providers you configured in step 1. +3. Start the scan. You can open any matched game while the scan continues to see the metadata RomM pulled. +4. When the scan finishes, click the RomM logo to return home. You'll see your platforms and the most recently added games. + +That's it — you're running RomM. From here: + +- Customise the on-disk layout: [Folder Structure](folder-structure.md) +- Add more users and lock down access: [Users & Roles](../administration/users-and-roles.md) +- Put RomM behind a reverse proxy with HTTPS: [Reverse Proxy](../install/reverse-proxy.md) +- Learn what all those settings mean: [Core Concepts](concepts.md) + +## Alternative: upload ROMs through the UI + +If your library isn't mounted (yet) or you're just adding a handful of files, the upload dialog is fine for small batches — not recommended for the initial import of a large collection, and it doesn't handle multi-file ROMs: + +1. Click **Upload** in the sidebar. +2. Choose the target platform, click **+ ADD**, and select the files to upload. +3. Click **Upload** and wait for the transfer to finish. + +upload dialog diff --git a/docs/index.md b/docs/index.md index e4702059..61b262fa 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- title: Introduction -description: An introduction to your beautiful, powerful, self-hosted rom manager and player +description: Your beautiful, powerful, self-hosted ROM manager and player. hide: - toc --- @@ -14,37 +14,77 @@ Welcome to the **RomM Project**, the premier self-hosted, open source ROM manage /// -RomM (ROM Manager) allows you to scan, enrich, and browse your game collection with a clean and responsive interface. With support for multiple platforms, various naming schemes, and custom tags, RomM is a must-have for anyone who plays on emulators. +RomM (ROM Manager) lets you scan, enrich, organise, and play your game collection from a clean web UI — with deep metadata from IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, LaunchBox, and more; in-browser play via EmulatorJS and Ruffle; companion apps for Android, handhelds, and desktop; and a first-class multi-user experience with OIDC SSO. -To get started with RomM, head over to the [Quick Start guide](Getting-Started/Quick-Start-Guide.md). +## Where do you want to go? -### Philosophy +
-At the heart of this project is a commitment to freedom, collaboration, and transparency. We believe that software should be built for the benefit of its users, rather than solely to maximize profit or serve the interests of a few stakeholders, ensuring that it doesn't manipulate, exploit, or prioritize data collection. +- :material-rocket-launch-outline: __I'm new — get me running__ -By offering RomM as a self-hosted, open-source solution, we ensure that everyone has the ability to manage their game collections on their own terms, and own their data, all without being tied to proprietary systems or services. + --- -**Rom is and will always be free and open-source software.** + Fifteen-minute Docker Compose walkthrough to a working RomM instance. -The core app is licensed under [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/), which requires that all modifications to the code be made available under the same license. This ensures that the community can benefit from and build upon the contributions of others, promoting trust and transparency. + [Quick Start →](getting-started/quick-start.md) -Other projects under the umbrella will be licensed under similar permissive licenses, such as [GNU GPLv3](https://choosealicense.com/licenses/gpl-3.0/) for software, or [CC0](https://choosealicense.com/licenses/cc0-1.0/) for documentation. +- :material-upload-outline: __I'm upgrading from 4.x__ -### Contributing + --- -RomM is a collaborative project, and we welcome contributions from the community. Our code is available on GitHub, and we encourage you to contribute to the project by submitting bug reports, feature requests, or pull requests. Please check the contribution guidelines in each project for more information. + Breaking changes, migrations, and the pre-flight checklist for RomM 5.0. -### Community + [Upgrade guide →](releases/upgrading-to-5.0.md) -We strive to create a safe and respectful space where everyone can contribute and benefit from the project by fostering a welcoming and inclusive environment for all users, regardless of their background or identity. +- :material-cog-outline: __I'm running RomM for my family/friends__ -Join us on Discord, where you can ask questions, submit ideas, get help, showcase your collection, and discuss RomM with other users. + --- + + Users, roles, OIDC, scheduled tasks, backups, reverse-proxy recipes. + + [Administration →](administration/index.md) + +- :material-controller-classic-outline: __I just want to play__ + + --- + + Library, collections, saves & states, Console Mode, ROM Patcher, Netplay. + + [Using RomM →](using/index.md) + +- :material-api: __I'm building on top of RomM__ + + --- + + REST API reference, WebSockets, device sync protocol, client tokens. + + [Developers →](developers/index.md) + +- :material-book-open-outline: __Just looking something up__ + + --- + + Environment variables, `config.yml`, scheduled tasks, glossary. + + [Reference →](reference/environment-variables.md) + +
+ +## Philosophy + +RomM is built for its users, not for shareholders. Self-hosted, open-source, no tracking, no upsells. The core app is licensed under [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/); other projects in the umbrella use permissive licenses ([GPLv3](https://choosealicense.com/licenses/gpl-3.0/) for software, [CC0](https://choosealicense.com/licenses/cc0-1.0/) for documentation). + +**RomM is and will always be free and open-source software.** + +## Community + +Join us on Discord to ask questions, share your setup, request features, or help other users. Code, issues, and releases live on [GitHub](https://github.com/rommapp/romm). diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index f5ae050c..e3f43c8f 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -1,10 +1,85 @@ --- -status: placeholder -wave: 1 +title: Docker Compose +description: Canonical Docker Compose reference for a production RomM 5.0 deployment. --- # Docker Compose -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +The canonical way to run RomM is with Docker Compose. This page describes the full reference stack — the shorter happy-path walkthrough lives in [Quick Start](../getting-started/quick-start.md). + +The RomM stack has three parts: + +1. **`romm`** — the application container (FastAPI backend, Vue frontend, nginx, and an embedded Redis/Valkey worker). +2. **A database** — MariaDB by default; MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. +3. **Redis or Valkey** — required for background tasks, session storage, and queue coordination. In the default `full-image` this runs inside the RomM container; for production you'll typically split it out. See [Redis or Valkey](redis-or-valkey.md). + +## Reference `docker-compose.yml` + +```yaml +--8<-- "quick-start.docker-compose.yml" +``` + +## Service reference + +### `romm` + +| Field | Value | Notes | +| --- | --- | --- | +| `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`. Pin to a specific tag (`5.0.0`) for production. See [Image Variants](image-variants.md) for `slim` vs `full`. | +| `ports` | `80:8080` | Container listens on `8080`. Expose through a reverse proxy in production — see [Reverse Proxy](reverse-proxy.md). | +| `volumes` | see below | RomM writes to four distinct paths inside the container. | +| `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | + +#### Volumes + +| Path inside container | Purpose | Backup priority | +| --- | --- | --- | +| `/romm/library` | Your ROM files. Typically mounted **read-only**. | No — this is your source data, back it up separately. | +| `/romm/assets` | User uploads: saves, states, uploaded screenshots. | **Critical.** Back this up with your DB. | +| `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low — can be re-downloaded on a rescan. | +| `/romm/config` | Holds `config.yml`. | **Critical.** Hand-tuned config, back it up. | + +See [Backup & Restore](backup-and-restore.md) for the full procedure. + +#### Core environment variables + +Minimal set — see the [Environment Variables reference](../reference/environment-variables.md) for the complete list. + +| Variable | What it does | +| --- | --- | +| `ROMM_AUTH_SECRET_KEY` | JWT signing secret. **Required.** Generate with `openssl rand -hex 32`. | +| `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | +| `ROMM_DB_DRIVER` | `mariadb` (default), `mysql`, or `postgresql`. | +| `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWD` | Only set if you're using an external Redis/Valkey instance. | +| Metadata provider creds | See [Metadata Providers](../administration/metadata-providers.md). | + +### `romm-db` (MariaDB) + +| Field | Value | Notes | +| --- | --- | --- | +| `image` | `mariadb:latest` | Pin to a major version (`mariadb:11`) for production. | +| `volumes` | `mysql_data:/var/lib/mysql` | Back this up — it's your entire catalogue. | +| `healthcheck` | `healthcheck.sh --connect --innodb_initialized` | RomM waits for this. | + +The default compose file uses MariaDB because it requires no extra driver configuration. To use PostgreSQL, swap the image for `postgres:16`, set `ROMM_DB_DRIVER=postgresql`, and point `DB_PORT` at `5432`. See [Databases](databases.md) for the full swap. + +## Production hardening + +The quick-start compose file is functional but not production-ready. Before exposing RomM to the internet: + +- **Pin image tags.** `rommapp/romm:latest` moves — use `rommapp/romm:5.0.0` (or whatever release you're on). +- **Use a reverse proxy with HTTPS.** The built-in nginx listens on `8080` and terminates plain HTTP. Put Traefik, Caddy, or nginx in front with TLS. See [Reverse Proxy](reverse-proxy.md). +- **Split out Redis/Valkey.** The `full-image` embeds Redis; for production run a dedicated container and set `REDIS_HOST`. See [Redis or Valkey](redis-or-valkey.md). +- **Set a non-default `MARIADB_ROOT_PASSWORD`.** And don't reuse it for `MARIADB_PASSWORD`. +- **Mount the library read-only** unless you need RomM to write into it: `- /path/to/library:/romm/library:ro`. +- **Use Docker secrets for credentials** if your orchestrator supports them. RomM recognises `ROMM_AUTH_SECRET_KEY_FILE` for loading the secret from a file mount. +- **Enable backups.** At minimum: daily `mysqldump` + rsync of `/romm/assets` and `/romm/config`. See [Backup & Restore](backup-and-restore.md). + +## Updating + +```sh +docker compose pull +docker compose up -d +``` + +Alembic migrations run automatically on startup. Read the [release notes](../releases/index.md) before every major-version bump — the 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). diff --git a/docs/install/image-variants.md b/docs/install/image-variants.md index 669d54d2..8934fe76 100644 --- a/docs/install/image-variants.md +++ b/docs/install/image-variants.md @@ -1,10 +1,44 @@ --- -status: placeholder -wave: 1 +title: Image Variants +description: Choose between the slim and full RomM container images. --- # Image Variants -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM publishes two production image variants. They're interchangeable at the config level — pick based on whether you want in-browser emulation. + +| Variant | Tag | Includes | Approx size | When to pick | +| --- | --- | --- | --- | --- | +| **Full** (default) | `rommapp/romm:latest`, `rommapp/romm:5.0.0` | Backend + frontend + **EmulatorJS** + **Ruffle** + embedded Redis + nginx with `mod_zip` | ~1.2 GB | You want in-browser play. 95% of users pick this. | +| **Slim** | `rommapp/romm:slim`, `rommapp/romm:5.0.0-slim` | Backend + frontend + embedded Redis + nginx with `mod_zip` | ~400 MB | Headless use (API + native-app clients only), bandwidth-constrained hosts, or when you're running emulators elsewhere. | + +Both variants are published on Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`). The GHCR images track the same tags and are a good choice if you rely on Docker Hub rate limits. + +## Switching variants + +Change the `image:` line in `docker-compose.yml` and recreate the container: + +```yaml +services: + romm: + image: rommapp/romm:5.0.0-slim # was :5.0.0 +``` + +```sh +docker compose up -d +``` + +No data migration is required — saves, states, metadata, and `config.yml` are the same across variants. + +## Dev images + +`rommapp/romm:dev-slim` and `rommapp/romm:dev-full` track the `master` branch with hot-reload-friendly entrypoints. Useful for testing changes against a real library — not recommended for anything you care about. See [Development Setup](../developers/development-setup.md). + +## What's actually different + +The two images share the same backend code, frontend bundle, nginx config, and entrypoint. The slim image omits: + +- **EmulatorJS** (~700 MB uncompressed) — the in-browser retro emulator bundle. +- **Ruffle** (~20 MB) — the Flash/Shockwave emulator. + +If you set `ENABLE_EMULATORJS=false` and `ENABLE_RUFFLE=false` on the full image, behaviour matches slim — only the image is larger. Setting them to `true` on the slim image will cause players to 404 at runtime. diff --git a/docs/install/reverse-proxy.md b/docs/install/reverse-proxy.md index 6551646b..655ff4cb 100644 --- a/docs/install/reverse-proxy.md +++ b/docs/install/reverse-proxy.md @@ -1,10 +1,193 @@ --- -status: placeholder -wave: 1 +title: Reverse Proxy +description: Put RomM behind Caddy, nginx, Traefik, or Nginx Proxy Manager with TLS. --- # Reverse Proxy -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +The RomM container listens on plain HTTP on port `8080`. For anything beyond `localhost` you should put it behind a reverse proxy that terminates TLS and forwards to the container. + +!!! tip "WebSockets are required" + RomM uses Socket.IO (both the general `/ws/socket.io` endpoint and the `/netplay/socket.io` endpoint) for live updates, scan progress, and Netplay. Every reverse-proxy recipe below keeps WebSocket support on — don't strip it out. + +The examples here assume your RomM container is reachable at `romm:8080` (by container name on a Docker network) or `192.168.1.100:8080` (by IP on the LAN). Swap to whatever's right for your setup. + +## Caddy + +Dead-simple, auto-HTTPS via Let's Encrypt. + +```caddyfile +romm.mysite.com { + encode zstd gzip + + header { + Strict-Transport-Security "max-age=31536000;" + X-XSS-Protection "1; mode=block" + X-Frame-Options "SAMEORIGIN" + X-Robots-Tag "noindex, nofollow" + -Server + -X-Powered-By + } + + reverse_proxy romm:8080 +} +``` + +If you just want HTTP on the LAN: + +```caddyfile +http://romm.mysite.com { + reverse_proxy romm:8080 +} +``` + +## Nginx + +### HTTP only + +```nginx +server { + listen 80 default_server; + server_name romm.mysite.com; + client_max_body_size 0; + + location / { + proxy_pass http://romm:8080; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +### HTTPS with HSTS + +```nginx +server { + listen 80 default_server; + server_name _; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name romm.mysite.com; + ssl_certificate /etc/ssl/romm/fullchain.pem; + ssl_certificate_key /etc/ssl/romm/privkey.pem; + client_max_body_size 0; + + location / { + proxy_pass http://romm:8080; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + server_tokens off; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + } +} +``` + +!!! note "`client_max_body_size 0`" + Required so large ROM uploads aren't rejected by nginx before they reach RomM. + +## Traefik + +### Dynamic configuration file + +```yaml +http: + routers: + romm: + entryPoints: + - websecure + rule: "Host(`romm.mysite.com`)" + middlewares: + - default-headers + - https-redirectscheme + tls: + certResolver: letsencrypt + service: romm + + services: + romm: + loadBalancer: + servers: + - url: "http://192.168.1.100:8080" + passHostHeader: true +``` + +### Docker Compose labels + +Add these to the `romm` service in your `docker-compose.yml`: + +```yaml +labels: + - "traefik.enable=true" + - "traefik.http.services.romm.loadbalancer.server.port=8080" + - "traefik.http.routers.romm.rule=Host(`romm.mysite.com`)" + - "traefik.http.routers.romm.entrypoints=websecure" + - "traefik.http.routers.romm.tls=true" + - "traefik.http.routers.romm.tls.certresolver=letsencrypt" +``` + +## Nginx Proxy Manager + +Items marked ❗ are important — RomM won't work right without them. + +### Details + +- **Domain Names**: `romm.mysite.com` +- **Scheme**: `http` +- **Forward Hostname/IP**: container hostname or LAN IP (e.g. `192.168.1.100`) +- **Forward Port**: `8080` +- **Cache Assets**: `off` +- **Block Common Exploits**: `on` +- **Websockets Support**: `on` ❗ +- **Access List**: as needed + +### SSL + +- **SSL Certificate**: Request a new SSL Certificate +- **Force SSL**: `on` +- **HTTP/2 Support**: `on` +- **HSTS Enabled**: `on` (after you've confirmed TLS works) +- **Email Address for Let's Encrypt**: your address +- **I Agree to the TOS**: `on` + +### Advanced — custom nginx configuration ❗ + +```nginx +proxy_max_temp_file_size 0; +``` + +Without that line, large downloads (bulk ROM zips, multi-disc games) will fail on NPM because nginx tries to buffer them to disk. + +| Details | SSL | Advanced | +| --- | --- | --- | +| ![image](https://github.com/user-attachments/assets/e106a8e9-8b27-41ef-8ba2-d43c3b68b269) | ![image2](https://github.com/user-attachments/assets/6c82c785-792a-410a-80f2-d95839cba47b) | ![image3](https://github.com/user-attachments/assets/566ae834-99b5-42f3-b46b-306b8f73b5b4) | + +## Set `ROMM_BASE_URL` behind HTTPS + +Once you're proxying through HTTPS, set `ROMM_BASE_URL` in the RomM container's environment so generated links (QR codes, invite links, OIDC redirects) use the public URL: + +```yaml +environment: + - ROMM_BASE_URL=https://romm.mysite.com +``` + +If you're also using OIDC, update `OIDC_REDIRECT_URI` to match — see [OIDC Setup](../administration/oidc/index.md). From de4cd64d25097a1c3270683a148a211cecb09d1d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 16:28:01 +0000 Subject: [PATCH 003/121] docs: draft Wave 1 install + administration deep-dives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second batch of Wave 1 content — the operator essentials for databases, cache, backups, SSO, and metadata providers. - install/databases.md: MariaDB/MySQL/Postgres/SQLite matrix with complete compose snippets for each; DB_QUERY_JSON guidance - install/redis-or-valkey.md: embedded vs external vs managed; Valkey drop-in; tuning notes (appendonly, maxmemory, no-eviction) - install/backup-and-restore.md: backup priority table; mariadb-dump and pg_dump recipes; nightly cron template; restore procedure; host-to-host migration (logical dump vs volume copy); upgrade pre-flight checklist - administration/oidc/index.md: rewritten hub with 5.0 OIDC_CLAIM_ROLES mapping, RP-initiated logout, autologin warnings, DISABLE_USERPASS_LOGIN footgun callout - administration/metadata-providers.md: all 13 providers (adds TheGamesDB, Libretro, gamelist.xml importer); priority config; filename metadata tags; popular combos Builds clean with --strict. --- docs/administration/metadata-providers.md | 215 +++++++++++++++++++++- docs/administration/oidc/index.md | 113 +++++++++++- docs/install/backup-and-restore.md | 168 ++++++++++++++++- docs/install/databases.md | 136 +++++++++++++- docs/install/redis-or-valkey.md | 104 ++++++++++- 5 files changed, 711 insertions(+), 25 deletions(-) diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md index 745db946..6c9dbe04 100644 --- a/docs/administration/metadata-providers.md +++ b/docs/administration/metadata-providers.md @@ -1,10 +1,215 @@ --- -status: placeholder -wave: 1 +title: Metadata Providers +description: Configure the thirteen metadata sources RomM supports — IGDB, ScreenScraper, MobyGames, RetroAchievements, SteamGridDB, Hasheous, PlayMatch, LaunchBox, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro. --- # Metadata Providers -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM pulls game metadata — titles, descriptions, cover art, screenshots, manuals, achievement data, completion times — from up to **thirteen** providers. You don't need all of them. This page covers the recommended combinations and per-provider setup. + +Configure providers either via env vars (below) or interactively in **Administration → Metadata Sources** in the UI. Scan priority (which provider wins when two disagree) is set in [`config.yml`](../reference/configuration-file.md) — see `scan.priority.metadata` and `scan.priority.artwork`. + +## Popular combos + +### ⭐ The Chef's Choice: Hasheous + IGDB + SteamGridDB + RetroAchievements + +- Covers 135+ popular systems. +- **Hasheous** does hash-based matching and proxies IGDB data (titles, descriptions, artwork). +- **IGDB** adds related games, screenshots, and broader metadata. +- **SteamGridDB** provides high-quality alternative cover art (opt-in per game via the "search cover" button). +- **RetroAchievements** overlays achievement progress. +- **Recommended default for most users.** + +![Hasheous + IGDB + SteamGridDB + RetroAchievements](../resources/metadata_providers/2dcovers.png) + +### ⭐ The French Connection: ScreenScraper + RetroAchievements + +- Covers 125+ popular systems. +- **ScreenScraper** provides titles, descriptions, cover art (2D + optional 3D + CD), screenshots, manuals. Also supports hash-based matching since RomM 4.4. +- **RetroAchievements** overlays achievement progress. +- **Pick this if you want to avoid anything Twitch/Amazon-owned.** + +![ScreenScraper + RetroAchievements](../resources/metadata_providers/3dboxes.png) + +### The Twitch Fanboy: IGDB + PlayMatch + +- Covers the 200+ systems IGDB knows about. +- IGDB-only metadata with PlayMatch's community-hosted hash-matching service bolted on for unmatched files. +- **Use if you specifically want a single-provider solution backed by IGDB.** + +### The Quick Starter: Hasheous only + +- Hash-based matching, fast scans, no API keys required. +- Proxies titles/descriptions/artwork from IGDB. +- **For users who want to avoid the IGDB/Twitch registration dance.** + +## Setup instructions + +### IGDB + +[IGDB](https://www.igdb.com/) (Internet Game Database) is a popular metadata source with coverage for 200+ systems: titles, descriptions, screenshots, related games, and more. + +Access requires a Twitch account and a phone number for 2FA. Up-to-date instructions live in the [IGDB API docs](https://api-docs.igdb.com/#account-creation). When registering your application in the Twitch Developer Portal: + +- **Name**: something unique — picking an existing name fails silently. Use `romm-`. +- **OAuth Redirect URLs**: `localhost` +- **Category**: Application Integration +- **Client Type**: Confidential + +Set `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` from the values Twitch generates. + +??? info "Screenshots" + ![IGDB Creation](../resources/metadata_providers/1-igdb.png) + ![IGDB Secret](../resources/metadata_providers/2-igdb.png) + +### ScreenScraper + +[ScreenScraper.fr](https://screenscraper.fr/) is a French provider with wide coverage and good artwork (2D, 3D, CD/cartridge). + +[Register](https://www.screenscraper.fr/membreinscription.php), then set `SCREENSCRAPER_USER` and `SCREENSCRAPER_PASSWORD`. + +### MobyGames + +Metadata, cover art, and screenshots. [Create an account](https://www.mobygames.com/user/register/), visit your profile, and follow the **API** link to request a key. Set `MOBYGAMES_API_KEY`. + +!!! important "MobyGames API is paid" + Access to the MobyGames API is a [paid, non-commercial-licensed feature](https://www.mobygames.com/info/api/#non-commercial). RomM will continue to support it, but we recommend ScreenScraper as a free alternative. + +### SteamGridDB + +[SteamGridDB](https://www.steamgriddb.com/) serves custom cover art for games and collections. It's not used by the scanner directly — it surfaces in the **Search Cover** button when you manually edit a game's artwork. + +Log in with a [Steam account](https://store.steampowered.com/join), go to your [API tab](https://www.steamgriddb.com/profile/preferences/api), and set `STEAMGRIDDB_API_KEY`. + +### RetroAchievements + +[RetroAchievements](https://retroachievements.org/) provides achievement data and hash matching. Generate a web API key from your RA [settings page](https://retroachievements.org/settings) and set `RETROACHIEVEMENTS_API_KEY`. Run an **Unmatched** scan on the platforms you want matched. + +Each RomM user also links their own RA username in their profile to sync personal progression — a new **Achievements** tab appears on the **Personal** data panel once linked. + +The RA database is cached locally; refresh frequency is controlled by `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` (default: 30). + +??? info "Screenshots" + ![RA API key](../resources/metadata_providers/1-ra.png) + ![RA details](../resources/metadata_providers/2-ra.png) + +### Hasheous + +[Hasheous](https://hasheous.org/) is free and open-source, does hash-based matching, and proxies IGDB data (no IGDB creds required on your side). Flag with `HASHEOUS_API_ENABLED=true`. + +### PlayMatch + +[PlayMatch](https://github.com/RetroRealm/playmatch) is a community-hosted hash-matching service. Pair it with IGDB for better matching on unmatched files. Flag with `PLAYMATCH_API_ENABLED=true`. + +### LaunchBox + +The [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) is a community-driven catalogue. RomM downloads the full database locally and matches on exact filenames — just like the LaunchBox desktop app. + +```yaml +environment: + - LAUNCHBOX_API_ENABLED=true + - ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=true + - SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON=0 5 * * * # default: 5am daily +``` + +Run at least one LaunchBox update (manually from the Scan page, or wait for the cron) before using it as a scan source — RomM won't match against an empty local DB. + +### TheGamesDB + +[TheGamesDB](https://thegamesdb.net/) is a free community database that doesn't require credentials. Flag with `TGDB_API_ENABLED=true`. + +### Flashpoint + +The [Flashpoint Project Database](https://flashpointproject.github.io/flashpoint-database/) covers 180,000+ Flash and browser-based games — the thing Ruffle is for. Flag with `FLASHPOINT_API_ENABLED=true`. Run an **Unmatched** scan to update existing platforms. + +### HowLongToBeat + +[HowLongToBeat](https://howlongtobeat.com/) adds game completion times (Main, Main + Extras, Completionist) to supported games. Flag with `HLTB_API_ENABLED=true`. + +A new **Time to Beat** tab appears on matched games' detail pages. + +### gamelist.xml (ES-DE / Batocera) + +If you came from EmulationStation / ES-DE / Batocera, you already have `gamelist.xml` files with metadata and media. RomM can parse these as a metadata source. + +Expected layout: + +```yaml +library/ +└─ roms/ + └─ gba/ + ├─ game_1.gba + ├─ game_2.gba + ├─ gamelist.xml + ├─ 3dboxes/ + ├─ covers/ + ├─ screenshots/ + └─ ... +``` + +#### ES-DE settings + +Two edits in the ES-DE settings file so ES-DE writes its metadata and media into the RomM-expected location: + +- Linux/macOS: `~/ES-DE/settings/es_settings.xml` +- Windows: `C:\Program Files\ES-DE\settings\es_settings.xml` + +```xml + + +``` + +`MediaDirectory` puts artwork next to ROMs; `LegacyGamelistFileLocation` writes `gamelist.xml` next to ROMs instead of in the ES-DE config folder. If you already have scraped assets, move the contents of `~/ES-DE/downloaded_media/` and `~/ES-DE/gamelists/` into the ROM folders. + +### Libretro + +Libretro's retro core metadata is used internally for platform mapping and fallback artwork — no env flag, no credentials. Nothing to configure; RomM uses it automatically when it knows the libretro core for a platform. + +## Metadata tags in filenames + +RomM honours inline tags in ROM filenames to force a match against a specific provider ID: + +| Tag | Provider | +| --- | --- | +| `(igdb-xxxx)` | [IGDB](https://www.igdb.com/) | +| `(moby-xxxx)` | [MobyGames](https://www.mobygames.com/) | +| `(ra-xxxx)` | [RetroAchievements](https://retroachievements.org/) | +| `(ssfr-xxxx)` | [ScreenScraper](https://screenscraper.fr/) | +| `(launchbox-xxxx)` | [LaunchBox](https://gamesdb.launchbox-app.com/) | +| `(hltb-xxxx)` | [HowLongToBeat](https://howlongtobeat.com/) | + +RomM will **not** rename your files to add these — they're opt-in, and renaming would conflict with other tooling that walks the filesystem. + +## Priority and conflict resolution + +When multiple providers return different values for the same field, the winner is determined by `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. Defaults: + +```yaml +scan: + priority: + metadata: + - igdb + - moby + - ss + - ra + - launchbox + - gamelist + - hasheous + - flashpoint + - hltb + artwork: + - igdb + - moby + - ss + - ra + - launchbox + - libretro + - gamelist + - hasheous + - flashpoint + - hltb +``` + +Reorder these lists to taste — for example, put `ss` first if you prefer ScreenScraper boxart, or move `hltb` up if you care about completion times more than descriptions. + +See the full [Configuration File reference](../reference/configuration-file.md) for everything `scan.priority` can do. diff --git a/docs/administration/oidc/index.md b/docs/administration/oidc/index.md index ce3cafaa..8768723d 100644 --- a/docs/administration/oidc/index.md +++ b/docs/administration/oidc/index.md @@ -1,10 +1,113 @@ --- -status: placeholder -wave: 1 +title: OIDC Setup +description: Wire RomM up to an OpenID Connect provider for SSO and centralised user management. --- # OIDC Setup -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +OpenID Connect (OIDC) lets users sign in to RomM through an external identity provider — Authelia, Authentik, Keycloak, PocketID, Zitadel, Okta, Auth0, or anything standards-compliant. Benefits: single sign-on across your homelab, no RomM-specific password to manage, centralised MFA, and on 5.0 you can map OIDC groups/claims to RomM roles. + +!!! note "OIDC is optional" + RomM has its own user system and works fine without OIDC. Enable OIDC when you already run an IdP and want RomM to follow suit. + +## How it works + +1. User clicks the OIDC login button on `/login`. +2. RomM redirects them to your provider. +3. They authenticate (password, passkey, MFA — whatever your provider enforces). +4. Provider redirects back to `{ROMM_BASE_URL}/api/oauth/openid` with an authorisation code. +5. RomM exchanges the code for an ID token, reads the user's email and role claims, and either creates a matching RomM user on the fly or logs an existing one in. +6. From there it's a normal RomM session — same cookies, same scope model. + +## Provider guides + +Pick your provider and follow the step-by-step. They all end with the same set of env vars on the RomM side — the guides just differ on how to register RomM as an application and where to find the client ID/secret. + +- [Authelia](authelia.md) — lightweight self-hosted IdP, great for homelabs. +- [Authentik](authentik.md) — full-featured open-source IdP with MFA and fancy flows. +- [Keycloak](keycloak.md) — the heavyweight standard; feature-complete. +- [PocketID](pocketid.md) — passkey-only, minimalist. +- [Zitadel](zitadel.md) — enterprise-grade open source with SAML + OIDC. + +Not listed? Any standards-compliant OIDC provider works — Okta, Auth0, Google Workspace, Microsoft Entra, etc. Use one of the above as a template and consult your provider's docs for the registration side. + +## Minimum RomM config + +Whichever provider you pick, set these in the `romm` service's environment: + +```yaml +environment: + - OIDC_ENABLED=true + - OIDC_PROVIDER= + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - ROMM_BASE_URL=https://romm.example.com # must match your reverse-proxy URL +``` + +`OIDC_REDIRECT_URI` must exactly match what you register at the provider — same scheme, host, path, no trailing slash. + +## Role mapping (5.0) + +By default, new OIDC users are provisioned as **Viewers**. To let your IdP assign roles based on group membership, set: + +```yaml +environment: + - OIDC_CLAIM_ROLES=groups # which claim to read + - OIDC_ROLE_VIEWER=romm-viewer,guests # group names → Viewer + - OIDC_ROLE_EDITOR=romm-editor + - OIDC_ROLE_ADMIN=romm-admin,platform-admins +``` + +On every login, RomM reads the claim named by `OIDC_CLAIM_ROLES` (often `groups`, sometimes `realm_access.roles` on Keycloak — check your provider's token output). Whichever role has a matching value wins; if nothing matches, the user stays/becomes a Viewer. + +Roles are re-evaluated on **every login**, so demoting someone on the IdP side takes effect the next time they sign in. + +## Autologin + +Bypass the RomM login page entirely — redirects straight to the IdP: + +```yaml +environment: + - OIDC_AUTOLOGIN=true +``` + +Useful when you want RomM to feel like a native part of your SSO stack. Combine with `DISABLE_USERPASS_LOGIN=true` to lock out local accounts entirely. + +!!! warning "Keep one local admin" + Don't set `DISABLE_USERPASS_LOGIN=true` without first confirming an admin account exists on the IdP side and can log in. If OIDC breaks and you've disabled local login, you're locked out until you fix the container env. + +## RP-Initiated Logout + +When set, hitting "Sign out" in RomM also signs the user out at the IdP: + +```yaml +environment: + - OIDC_RP_INITIATED_LOGOUT=true + - OIDC_END_SESSION_ENDPOINT=https://auth.example.com/application/o/end-session/ +``` + +The endpoint URL is provider-specific; the per-provider guides list it. + +## Username source + +By default the local part of the email (the bit before `@`) becomes the RomM username. Override with: + +```yaml +environment: + - OIDC_USERNAME_ATTRIBUTE=preferred_username +``` + +## Important notes + +- **Email must match** between OIDC and any existing RomM account, otherwise OIDC creates a new account alongside the old one. +- **HTTPS is required** in production — OIDC will refuse to redirect to a plain-HTTP `ROMM_BASE_URL`. +- **Clock skew**: large drift between the RomM host and IdP will cause ID-token validation to fail. Run NTP. + +## Troubleshooting + +Common failures and fixes live in [Authentication Troubleshooting](../../troubleshooting/authentication.md). Two of the usual suspects: + +- `redirect_uri_mismatch` — `OIDC_REDIRECT_URI` differs from what's registered at the provider. Even a trailing slash matters. +- User created but stuck at Viewer — check `OIDC_CLAIM_ROLES` points at a claim that actually exists in the token, and the group names match exactly (case-sensitive). diff --git a/docs/install/backup-and-restore.md b/docs/install/backup-and-restore.md index 88751be2..6ccf9cd5 100644 --- a/docs/install/backup-and-restore.md +++ b/docs/install/backup-and-restore.md @@ -1,10 +1,168 @@ --- -status: placeholder -wave: 1 +title: Backup & Restore +description: Protect your RomM install against data loss, and move it between hosts. --- # Backup & Restore -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +This page covers both routine backups and migrating RomM to a new host — same procedure, different frequency. + +## What to back up + +| Path / volume | What's in it | Backup? | +| --- | --- | --- | +| **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical** — back this up nightly. | +| **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical** — back this up nightly. | +| **`/romm/config`** | `config.yml` and any custom overrides | **Critical** — rarely changes, but small and painful to recreate. | +| `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority — can be re-downloaded on a rescan. Including it speeds up recovery. | +| `/redis-data` | Task queue state | Low priority — in-flight tasks only; lost tasks can be re-run. | +| **`/romm/library`** | Your ROM files | Back this up **separately** — it's your source data and you should already have a backup strategy for it independent of RomM. | + +## Routine backup + +!!! warning "Always stop the stack or use consistent snapshots" + A live `cp -r` of the DB volume can copy an inconsistent state. Either bring the stack down briefly, or use `mysqldump` / `pg_dump` for a consistent logical dump. + +### DB dump (MariaDB / MySQL) + +```sh +docker exec romm-db mariadb-dump \ + --user=romm-user --password= \ + --single-transaction --databases romm \ + > romm-db-$(date +%F).sql +``` + +### DB dump (PostgreSQL) + +```sh +docker exec romm-db pg_dump \ + --username=romm-user --dbname=romm \ + > romm-db-$(date +%F).sql +``` + +### Assets + config + +Copy the host paths you mounted as `/romm/assets` and `/romm/config`. Incremental rsync is fine: + +```sh +rsync -a --delete /srv/romm/assets/ /backup/romm/assets/ +rsync -a --delete /srv/romm/config/ /backup/romm/config/ +``` + +### Putting it together + +A minimal nightly cron: + +```sh +#!/usr/bin/env bash +set -euo pipefail +STAMP=$(date +%F) +DEST=/backup/romm + +docker exec romm-db mariadb-dump --user=romm-user --password=$DB_PASSWD \ + --single-transaction --databases romm | gzip > "$DEST/db-$STAMP.sql.gz" + +rsync -a --delete /srv/romm/assets/ "$DEST/assets/" +rsync -a --delete /srv/romm/config/ "$DEST/config/" + +# keep 14 days of DB dumps +find "$DEST" -maxdepth 1 -name 'db-*.sql.gz' -mtime +14 -delete +``` + +Offsite it however you already do — rclone to B2/S3, restic, borg, Proxmox Backup Server — RomM doesn't care. + +## Restore + +### Into an empty install + +1. Start a fresh RomM stack with the same `ROMM_AUTH_SECRET_KEY` (if the secret changes, all sessions and invite links are invalidated). Point it at empty volumes. +2. Wait for the first-run setup to finish, then stop the stack: `docker compose stop`. +3. Restore the DB: + ```sh + docker exec -i romm-db mariadb --user=root --password= romm < romm-db-2026-04-15.sql + # or for Postgres: + docker exec -i romm-db psql --username=romm-user --dbname=romm < romm-db-2026-04-15.sql + ``` +4. Restore `assets/` and `config/` to the mounted host paths. +5. Restart: `docker compose start`. RomM will run any pending Alembic migrations automatically, so restoring a dump from an older version into a newer install is safe. + +### In place (oh no, something's broken) + +Same steps, but skip step 1 — stop the stack, swap the DB dump back in, restart. Keep the dump you're about to overwrite: `mv current-broken.sql rollback.sql`. + +## Moving RomM to a new host + +Same mechanics as a restore. Two approaches: + +### Option A: logical dump + host-to-host copy (recommended) + +Cleaner, version-independent, works across DB driver changes. + +```sh +# on old host +docker exec romm-db mariadb-dump --user=romm-user --password=$DB_PASSWD \ + --single-transaction --databases romm > romm-db.sql +tar czf romm-data.tar.gz /srv/romm/assets /srv/romm/config + +# transfer both files to the new host, then on the new host: +docker compose up -d romm-db # start DB container first +docker exec -i romm-db mariadb --user=root --password=$ROOT_PW romm < romm-db.sql +tar xzf romm-data.tar.gz -C / +docker compose up -d # start the rest +``` + +### Option B: Docker-volume copy + +If you're lazy and on the same Docker version, copy volume data directly. + +```sh +# on old host, stop RomM first +docker compose down + +docker volume ls +# DRIVER VOLUME NAME +# local romm_mysql_data +# local romm_romm_redis_data +# local romm_romm_resources + +docker volume inspect romm_mysql_data | grep Mountpoint +# "Mountpoint": "/var/lib/docker/volumes/romm_mysql_data/_data" + +sudo rsync -aHAX /var/lib/docker/volumes/romm_mysql_data/ new-host:/opt/romm/mysql_data/ +sudo rsync -aHAX /var/lib/docker/volumes/romm_romm_redis_data/ new-host:/opt/romm/redis_data/ +sudo rsync -aHAX /var/lib/docker/volumes/romm_romm_resources/ new-host:/opt/romm/resources/ + +# on new host, bind-mount those paths instead of named volumes: +# volumes: +# - /opt/romm/mysql_data/_data:/var/lib/mysql +# - /opt/romm/redis_data/_data:/redis-data +# - /opt/romm/resources/_data:/romm/resources +``` + +Works, but binds you to matching the old host's Docker layout. Use Option A unless you have a reason not to. + +## Verifying a backup is actually restorable + +A backup you haven't restored is a hope, not a backup. Once a quarter, spin up a throwaway RomM stack from a recent backup: + +```sh +# in a scratch directory +cp docker-compose.yml ./test-compose.yml +# point DB + assets volumes at test paths +docker compose -f test-compose.yml up -d +# restore the latest dump into it +# log in, check that users/collections/saves are present +docker compose -f test-compose.yml down -v +``` + +## Upgrade pre-flight + +Before upgrading to a new RomM major version: + +1. Stop the stack: `docker compose stop`. +2. Take a fresh DB dump (above). +3. Snapshot the assets + config volumes. +4. Start back up: `docker compose start`. +5. Pull the new image and upgrade. + +If the upgrade blows up, restore the dump + snapshot. For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md) first — there are breaking changes. diff --git a/docs/install/databases.md b/docs/install/databases.md index ac41e0c2..c325b3f7 100644 --- a/docs/install/databases.md +++ b/docs/install/databases.md @@ -1,10 +1,136 @@ --- -status: placeholder -wave: 1 +title: Databases +description: Supported database drivers for RomM, connection strings, and recommendations. --- # Databases -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM uses SQLAlchemy + Alembic for persistence. Four drivers are supported; pick based on what you already run. + +| Driver | `ROMM_DB_DRIVER` | Image | Default port | Notes | +| --- | --- | --- | --- | --- | +| **MariaDB** (default, recommended) | `mariadb` | `mariadb:11` | `3306` | What the reference compose uses. Well-tested. | +| **MySQL** | `mysql` | `mysql:8` | `3306` | Largely interchangeable with MariaDB for RomM. | +| **PostgreSQL** | `postgresql` | `postgres:16` | `5432` | Supported. Use if you already run Postgres. | +| **SQLite** | `sqlite` | _(file on disk)_ | — | Dev/tiny deployments only. Not recommended for anything multi-user or larger than a few hundred games. | + +RomM runs Alembic migrations automatically on startup — no manual step when upgrading. + +## MariaDB (default) + +This is what the [reference Compose](docker-compose.md) sets up. No extra config beyond filling in the passwords. + +```yaml +services: + romm: + environment: + - ROMM_DB_DRIVER=mariadb # optional; this is the default + - DB_HOST=romm-db + - DB_PORT=3306 + - DB_NAME=romm + - DB_USER=romm-user + - DB_PASSWD= + + romm-db: + image: mariadb:11 + environment: + - MARIADB_ROOT_PASSWORD= + - MARIADB_DATABASE=romm + - MARIADB_USER=romm-user + - MARIADB_PASSWORD= + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + start_period: 30s + interval: 10s + timeout: 5s + retries: 5 +``` + +## MySQL + +Identical compose to MariaDB, but swap the image and the healthcheck: + +```yaml +services: + romm: + environment: + - ROMM_DB_DRIVER=mysql + - DB_HOST=romm-db + - DB_PORT=3306 + - DB_NAME=romm + - DB_USER=romm-user + - DB_PASSWD= + + romm-db: + image: mysql:8 + environment: + - MYSQL_ROOT_PASSWORD= + - MYSQL_DATABASE=romm + - MYSQL_USER=romm-user + - MYSQL_PASSWORD= + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 +``` + +## PostgreSQL + +```yaml +services: + romm: + environment: + - ROMM_DB_DRIVER=postgresql + - DB_HOST=romm-db + - DB_PORT=5432 + - DB_NAME=romm + - DB_USER=romm-user + - DB_PASSWD= + + romm-db: + image: postgres:16 + environment: + - POSTGRES_DB=romm + - POSTGRES_USER=romm-user + - POSTGRES_PASSWORD= + volumes: + - pg_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U romm-user -d romm"] + interval: 10s + timeout: 5s + retries: 5 +``` + +## SQLite (not recommended) + +Set `ROMM_DB_DRIVER=sqlite`. The DB file lives at `{ROMM_BASE_PATH}/database`. No separate container required — useful for a laptop demo or a one-user install on a low-power box. Don't use this for anything you care about: + +- No concurrent writers → scans and API calls block each other. +- The file can corrupt if the container is killed mid-write. +- Migrating to MariaDB/Postgres later requires a dump/reload. + +## Extra connection parameters + +`DB_QUERY_JSON` takes a JSON blob of extra parameters appended to the connection string. Useful for enabling TLS to an external DB, setting a connection timeout, or hitting a non-default port: + +```yaml +environment: + - DB_QUERY_JSON={"ssl": "true", "connect_timeout": "5"} +``` + +Exact keys depend on the driver — see SQLAlchemy / the driver's docs. + +## Which should I pick? + +- **Sticking with defaults?** MariaDB. That's what the reference compose uses and what the team tests against. +- **Already run Postgres?** Postgres. No reason to add a second DB engine. +- **Single-user laptop demo?** SQLite is fine; upgrade before adding anyone else. +- **External managed DB?** Any of MariaDB / MySQL / Postgres. Point `DB_HOST` at it and configure TLS via `DB_QUERY_JSON`. + +Don't switch DB drivers on a running install without a plan — migrating the data requires a dump + reload, covered in [Backup & Restore](backup-and-restore.md). diff --git a/docs/install/redis-or-valkey.md b/docs/install/redis-or-valkey.md index fcd9d02d..ac9bd159 100644 --- a/docs/install/redis-or-valkey.md +++ b/docs/install/redis-or-valkey.md @@ -1,10 +1,104 @@ --- -status: placeholder -wave: 1 +title: Redis or Valkey +description: Why RomM needs Redis, how it's run, and when to split it out. --- # Redis or Valkey -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM needs a Redis-protocol server. It's used for: + +- **Session storage** — login sessions, CSRF tokens. +- **Background task queue** — scans, metadata syncs, sync operations run through RQ (Redis Queue). +- **Cache** — metadata lookups, heartbeat data, paired-device tokens, rate-limit counters. +- **Socket.IO** — multi-instance pubsub for live updates (only relevant if you're running more than one RomM container). + +Both **Redis** and **Valkey** (the open-source Redis fork maintained by the Linux Foundation after Redis Inc.'s license change) are supported. They're interchangeable — Valkey is a drop-in wire-compatible replacement. Pick either. + +## Option A: embedded (default) + +The default `full-image` container runs a Redis server inside itself. Zero config, fine for single-instance deployments. The `REDIS_HOST` / `REDIS_PORT` defaults (`localhost:6379`) point at the embedded instance; leave them alone. + +**Data** is persisted to `/redis-data` inside the container. In the reference compose, that's mounted to a Docker volume (`romm_redis_data`) so queue state survives restarts. Don't skip the volume — you'll lose in-flight tasks on every `docker compose up -d`. + +## Option B: external Redis container + +Better for: + +- Any production instance you care about. +- Multiple RomM replicas sharing state. +- Homelabs that already run a Redis box and want consistency. + +```yaml +services: + romm: + environment: + - REDIS_HOST=romm-redis + - REDIS_PORT=6379 + - REDIS_PASSWD= + depends_on: + romm-redis: + condition: service_healthy + + romm-redis: + image: redis:7-alpine + command: ["redis-server", "--requirepass", "", "--appendonly", "yes"] + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "", "ping"] + interval: 10s + timeout: 3s + retries: 5 + +volumes: + redis_data: +``` + +### Valkey drop-in + +```yaml + romm-redis: + image: valkey/valkey:7-alpine + command: ["valkey-server", "--requirepass", "", "--appendonly", "yes"] + # everything else the same +``` + +## Option C: external managed Redis + +If you've got an AWS ElastiCache / Upstash / Redis Cloud instance, point RomM at it: + +```yaml +environment: + - REDIS_HOST=my-redis.cache.amazonaws.com + - REDIS_PORT=6379 + - REDIS_USERNAME=romm # omit if your Redis uses legacy auth + - REDIS_PASSWORD= + - REDIS_SSL=true # for managed Redis, almost always yes + - REDIS_DB=0 # separate RomM from other apps on the same instance +``` + +The full list of Redis env vars lives in [Environment Variables](../reference/environment-variables.md). + +## Tuning + +RomM's Redis usage is light — sessions, a queue, a bit of cache. Defaults are fine for anything up to tens of thousands of ROMs and a few dozen users. Things to know: + +- **`appendonly yes`** is strongly recommended (what the reference compose uses). Without it, a crash loses any task currently in the queue. +- **RDB snapshotting** is fine on top; `save 60 1` gives you a minutely snapshot. +- **Memory**: RomM doesn't hold large blobs in Redis. A 256 MB `maxmemory` is plenty for most instances. +- **Key eviction**: leave `maxmemory-policy` alone (default: `noeviction`). RomM doesn't tolerate silent key loss — sessions would drop and running tasks would lose state. + +## Verifying it works + +```sh +docker exec romm redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWD" ping +# PONG +``` + +If you see `PONG` from the RomM container, you're good. If not, check: + +- That the DNS name in `REDIS_HOST` resolves from the RomM container (use `docker exec romm getent hosts romm-redis`). +- That the password is correct — `redis-cli -a` will say `NOAUTH` if wrong. +- That `REDIS_SSL=true` matches whether the server actually requires TLS. + +Debugging further: see the Redis line in `docker logs romm` at startup; RomM logs the connection target. From 69eed3e5a1b4dd4a89f8bf11ac21e902844831b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 16:35:12 +0000 Subject: [PATCH 004/121] docs: draft Wave 1 administration section (hub + 5 deep pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the core Administration story — every page the operator reaches from the profile drawer (minus the niche-feature pages: server-stats, observability, firmware-management, ssh-sync). - administration/index.md: hub landing with role primer and first-time operator checklist - administration/invitations-and-registration.md: invite flow, public registration warning, role assignment per sign-up path - administration/authentication.md: session/password config, DISABLE_USERPASS_LOGIN footgun, kiosk mode, download-auth bypass, revocation runbook - administration/scanning-and-watcher.md: all 6 scan modes, scheduled vs watcher tradeoffs, exclusions, region/language priority, when NOT to enable the watcher - administration/scheduled-tasks.md: narrative wrapper around the autogenerated task table; per-task purpose; triggering/monitoring; small-host tuning - administration/administration-page.md: full UI tour of the profile drawer with role-based visibility cheat sheet Builds clean with --strict. --- docs/administration/administration-page.md | 119 +++++++++++++++- docs/administration/authentication.md | 122 +++++++++++++++- docs/administration/index.md | 67 ++++++++- .../invitations-and-registration.md | 82 ++++++++++- docs/administration/scanning-and-watcher.md | 134 +++++++++++++++++- docs/administration/scheduled-tasks.md | 132 ++++++++++++++++- 6 files changed, 626 insertions(+), 30 deletions(-) diff --git a/docs/administration/administration-page.md b/docs/administration/administration-page.md index 786d00b8..21f69a16 100644 --- a/docs/administration/administration-page.md +++ b/docs/administration/administration-page.md @@ -1,10 +1,119 @@ --- -status: placeholder -wave: 1 +title: Administration Page +description: A tour of the in-app Administration UI — where every operator control lives. --- # Administration Page -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Click your **profile avatar** (top right, any page) to open the settings drawer. The links you see depend on your role. Admins see everything; Editors and Viewers see a subset. + +This page is a map of what's behind each link. The deep mechanics of each feature live on their own pages — this is where to click. + +## The drawer + +| Link | Who sees it | What's there | +| --- | --- | --- | +| **Profile** | Everyone | Change own username, email, password, avatar. Link a RetroAchievements account and sync achievements. | +| **User Interface** | Everyone | Locale, theme (dark/light/auto), game card layout, home dashboard ribbons, collection display settings. | +| **Library Management** | Editors + Admins | Platform bindings & version mappings, missing-ROMs tool, library folder settings. | +| **Metadata Sources** | Admins | Credentials for the 13 metadata providers; scan priority. | +| **Administration** | Admins | Users, Client API Tokens, Tasks. The main admin hub. | +| **Client API Tokens** | Everyone (own tokens) | Each user's personal API tokens. Admins see a separate "all tokens" view under Administration. | +| **Server Stats** | Admins | Numbers — platforms, games, saves, states, screenshots, disk usage. | +| **About** | Everyone | RomM version, links to Discord / GitHub / docs. | + +## Profile + +The thing every user touches. + +- **Username / email / password** — self-serve changes. Password changes require the current password. +- **Avatar** — upload a small image; displayed next to your name everywhere. +- **RetroAchievements** — set your RA username to link accounts; "Sync now" pulls fresh progression data. + +See [Users & Roles](users-and-roles.md) for what role-specific self-serve is allowed. + +## User Interface + +Per-user UI preferences. Stored in the user's row + localStorage, not in `config.yml`. + +- **Language** — 19 locales supported; see [Languages](../using/languages.md) for the list. +- **Theme** — Dark, Light, or Auto (follows OS preference). Palette overrides via `extra_css` are operator-level. +- **Game card layout** — cover style (2D, 3D boxart, poster), info-density, the `vanilla-tilt` 3D hover effect on/off. +- **Home dashboard ribbons** — show/hide "Recently Added", "Continue Playing", "Collections", etc. +- **Virtual collections** — enable/disable auto-generated groupings (by genre, developer, year, etc.). + +## Library Management + +Editor-grade tools for catalogue hygiene. + +- **Platform bindings** — map a filesystem folder name (`super_nintendo/`) to a platform slug (`snes`). Mirrors `system.platforms` in `config.yml`. +- **Platform versions** — some platforms have multiple IGDB "versions" (e.g. Mega Drive vs Genesis). Pin which one RomM uses for lookups. +- **Missing ROMs** — a filter/table view showing DB entries whose files are gone. Bulk-delete from here, or run the [Cleanup Missing ROMs](scheduled-tasks.md#cleanup-missing-roms-manual) task. + +## Metadata Sources + +Admin-only. Per-provider credentials, test buttons, and the priority-ordering drag-and-drop UI. Equivalent to editing the `*_CLIENT_ID` / `*_API_KEY` env vars and `scan.priority` in `config.yml`, but interactive. + +Full provider details in [Metadata Providers](metadata-providers.md). + +## Administration + +The main admin hub. Three sub-panels: + +### Users + +- Table of all users with role, last login, creation date. +- **Add** — manual user creation with username + email + password + role. +- **Invite** — generate an invite link. See [Invitations & Registration](invitations-and-registration.md). +- **Edit** — change username, email, role, password. Reset password by typing a new one. +- **Delete** — red trash icon. RomM won't let you delete yourself or the last admin. + +### Client API Tokens + +- Table of every token on the server (admin view; users see only their own via their Profile). +- Filter by user. Revoke any token. +- See [Authentication → Client API Tokens](authentication.md#client-api-tokens) for the create-your-own flow. + +### Tasks + +- Status of every scheduled / manual / watcher task — queued, running, idle, failed. +- **Run** button per task (requires `tasks.run` scope). +- See [Scheduled Tasks](scheduled-tasks.md) for what each one does. + +## Server Stats + +Admin-only. A dashboard of counts and sizes: + +- Total platforms, games, saves, states, screenshots. +- Total disk footprint (library + resources + assets). +- Per-platform breakdown — handy for spotting a platform that's ballooned. + +Full details in [Server Stats](server-stats.md). + +## Keyboard shortcuts + +A few useful ones wherever the drawer is open: + +| Key | Action | +| --- | --- | +| `Esc` | Close the drawer. | +| `g h` | Go home. | +| `g s` | Open the Search page. | +| `g c` | Focus the Scan button in the sidebar. | + +Full shortcut reference is on the [Using RomM](../using/index.md) page. + +## Role-based visibility cheat sheet + +| Section | Viewer | Editor | Admin | +| --- | :---: | :---: | :---: | +| Profile | ✓ | ✓ | ✓ | +| User Interface | ✓ | ✓ | ✓ | +| Client API Tokens (own) | ✓ | ✓ | ✓ | +| About | ✓ | ✓ | ✓ | +| Library Management | — | ✓ | ✓ | +| Metadata Sources | — | — | ✓ | +| Administration → Users | — | — | ✓ | +| Administration → Tokens (all) | — | — | ✓ | +| Administration → Tasks | — | — | ✓ | +| Server Stats | — | — | ✓ | diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index c133a664..09e2271b 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -1,10 +1,122 @@ --- -status: placeholder -wave: 1 +title: Authentication +description: Configure how users sign in — sessions, password policy, client tokens, OIDC hooks, and kiosk mode. --- # Authentication -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +This page is the **operator-side** authentication reference: knobs you turn on the server to control how people sign in. The **client-side** reference — "how do I actually authenticate an API call?" — is in [API Authentication](../developers/api-authentication.md). + +Authentication flows RomM supports: + +- **Username + password** (default) — local account, bcrypt-hashed, stored in the DB. +- **OIDC** — single sign-on via an external IdP. See [OIDC Setup](oidc/index.md). +- **Client API Tokens** — long-lived per-user tokens for companion apps and scripts. +- **Device pairing** — short codes for bootstrapping a token onto a handheld. Covered in [Client API Tokens](../ecosystem/client-api-tokens.md). +- **Kiosk mode** — unauthenticated read-only access. Toggle for public demos / shared terminals. + +## Session config + +Sessions are cookie-based and stored in Redis. Relevant env vars: + +| Variable | Default | What it controls | +| --- | --- | --- | +| `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually** — it invalidates every active session and every outstanding invite link. | +| `ROMM_AUTH_SECRET_KEY_FILE` | — | Alternative: read the secret from a file. Useful with Docker secrets. | +| `SESSION_MAX_AGE_SECONDS` | 86400 (24 h) | How long a session cookie lives before the user has to sign in again. | +| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | 900 (15 min) | Short-lived OAuth2 access token TTL. | +| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | 2592000 (30 d) | Refresh token TTL for OAuth2. | +| `DISABLE_CSRF_PROTECTION` | `false` | Disable CSRF middleware. Only do this behind a trusted reverse proxy that strips unwanted cross-origin traffic. | + +## Local (username + password) + +The default. Accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md). Passwords are bcrypt-hashed — RomM does not log or store plaintext passwords at any point. + +**Disable local password login entirely** (force OIDC-only): + +```yaml +environment: + - DISABLE_USERPASS_LOGIN=true +``` + +!!! warning "Keep a way in" + Before setting `DISABLE_USERPASS_LOGIN=true`, confirm that at least one Admin account can sign in via OIDC and reach the Administration page. If OIDC breaks and you've already disabled local login, your only way in is editing the container env. + +### Admin-triggered password reset + +Until email-based self-serve reset lands, admins set passwords manually: + +**Administration → Users → Edit → New password → Save.** + +The next login on that account will use the new password; existing sessions for that user remain valid until they expire — revoke them explicitly by deleting all of the user's Client API Tokens if that's a concern. + +### Self-serve password reset + +Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA — the UI path exists and will light up once email config is exposed.) + +## OIDC + +See [OIDC Setup](oidc/index.md) for the full walkthrough. One-liner config sketch: + +```yaml +environment: + - OIDC_ENABLED=true + - OIDC_PROVIDER=keycloak + - OIDC_CLIENT_ID=... + - OIDC_CLIENT_SECRET=... + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid +``` + +When OIDC is configured, the login page grows an "OIDC" button. Set `OIDC_AUTOLOGIN=true` to redirect straight to the IdP without the user having to click it. + +## Client API Tokens + +For anything long-lived — a companion app, a cron job, a script — use **Client API Tokens** instead of storing a password. + +Create from **Administration → Client API Tokens**. Each token: + +- Belongs to a specific user. +- Carries a **subset** of that user's scopes (you choose which at creation time). +- Has an optional expiry (no expiry = never expires until manually revoked). +- Can be "paired" to a device via a short code (covered in [Client API Tokens](../ecosystem/client-api-tokens.md)). + +Each user gets up to 25 active tokens. Revoke from the same page. The API side — "how do I send this thing in a request?" — lives in [API Authentication](../developers/api-authentication.md). + +## Kiosk mode + +Turns every GET endpoint into unauthenticated read-only access — anyone reaching the instance can browse, but nobody can write, scan, upload, or manage users. + +```yaml +environment: + - KIOSK_MODE=true +``` + +Appropriate for: + +- Shared-terminal demos. +- Public-facing "display" instances (e.g. a wall-mounted browse-only catalogue). +- `demo.romm.app`. + +Authenticated users (when you do sign in) still see their full role; kiosk only affects anonymous traffic. + +## Download-endpoint auth bypass + +```yaml +environment: + - DISABLE_DOWNLOAD_ENDPOINT_AUTH=true +``` + +Skips auth on `GET /api/roms/{id}/content/…` and the firmware download endpoint. Exists so third-party apps that can't carry a bearer header (dumb emulators loading a ROM by URL) can still pull files. + +**Only enable this when the public internet can't reach RomM directly** — i.e. there's auth or an IP allowlist at the reverse-proxy layer. Otherwise you've just made your library world-downloadable. + +## Revoking access + +To fully cut a user off: + +1. **Administration → Users → Edit** → set password to something random and toggle to Viewer (so even if they're mid-session they can't do damage). +2. Delete all their Client API Tokens (**Administration → Client API Tokens**, filter by user). +3. Delete the user. + +Steps 1 + 2 together ensure any in-flight session and any token-based companion app lose access immediately. Step 3 removes the account. diff --git a/docs/administration/index.md b/docs/administration/index.md index 6535cfa0..38796e71 100644 --- a/docs/administration/index.md +++ b/docs/administration/index.md @@ -1,10 +1,67 @@ --- -status: placeholder -wave: 1 +title: Administration +description: Running RomM for yourself and others — users, auth, metadata, scans, tasks, backups. --- # Administration -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Administration is everything you do **as the operator** of a RomM instance: managing accounts, controlling access, configuring metadata sources, scheduling scans, watching the library for changes, monitoring the server, and keeping data safe. + +The end-user equivalent — how to actually play the games, build collections, upload saves — lives in [Using RomM](../using/index.md). + +## Where things live + +### Users & access + +- **[Users & Roles](users-and-roles.md)** — roles, the 19-scope model, how permissions add up. +- **[Invitations & Registration](invitations-and-registration.md)** — invite links, public signup, first-user setup. +- **[Authentication](authentication.md)** — session config, password reset, Client API Tokens for devices. +- **[OIDC Setup](oidc/index.md)** — Authelia, Authentik, Keycloak, PocketID, Zitadel — SSO + role mapping. + +### Content & library + +- **[Metadata Providers](metadata-providers.md)** — all 13 providers, credentials, priority ordering. +- **[Scanning & Watcher](scanning-and-watcher.md)** — how scans work, scan modes, filesystem watcher. +- **[Firmware Management](firmware-management.md)** — BIOS/firmware uploads for emulation. + +### Operations + +- **[Scheduled Tasks](scheduled-tasks.md)** — what runs in the background and how to tune it. +- **[Server Stats](server-stats.md)** — the stats page and what its numbers mean. +- **[Observability](observability.md)** — logs, Sentry, OpenTelemetry, `/heartbeat`. +- **[SSH Sync](ssh-sync.md)** — push/pull sync to handhelds and other devices. +- **[Administration Page](administration-page.md)** — the in-app admin UI tour. + +### Configuration + +- **[Environment Variables](../reference/environment-variables.md)** — every env var, grouped by area. +- **[Configuration File](../reference/configuration-file.md)** — the `config.yml` schema. + +### Keeping data safe + +- **[Backup & Restore](../install/backup-and-restore.md)** — routine backups, restore drill, host migration. + +## The role model in thirty seconds + +Three built-in roles, all backed by the same scope system: + +| Role | Summary | +| --- | --- | +| **Admin** | Full control. User management, task execution, every scope. First user always gets this. | +| **Editor** | Curate the library — edit ROMs, platforms, collections, upload firmware. No user management. | +| **Viewer** | Play games, manage own saves/states/profile. Read-only everywhere else. | + +Full scope matrix in [Users & Roles](users-and-roles.md). + +## The operator checklist + +First time running RomM for someone else? Do these, in this order: + +1. **Set `ROMM_AUTH_SECRET_KEY`** (via `openssl rand -hex 32`) and keep it forever. Changing it invalidates every session and invite link. +2. **Put it behind HTTPS.** See [Reverse Proxy](../install/reverse-proxy.md). Set `ROMM_BASE_URL` to the public URL. +3. **Set up at least one metadata provider** before the first scan. See [Metadata Providers](metadata-providers.md). +4. **Run the first scan** from the Scan page. See [Scanning & Watcher](scanning-and-watcher.md). +5. **Decide on invitations vs open registration.** See [Invitations & Registration](invitations-and-registration.md). +6. **Wire up OIDC** if you run an IdP. See [OIDC Setup](oidc/index.md). +7. **Configure backups.** See [Backup & Restore](../install/backup-and-restore.md). Test the restore before you need it. +8. **Pin the image tag** (`rommapp/romm:5.0.0`, not `:latest`) and read release notes before each upgrade. diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md index 59299726..fedd27b2 100644 --- a/docs/administration/invitations-and-registration.md +++ b/docs/administration/invitations-and-registration.md @@ -1,10 +1,82 @@ --- -status: placeholder -wave: 1 +title: Invitations & Registration +description: Inviting users, public signup, the first-user flow, and role assignment at sign-up. --- # Invitations & Registration -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Three ways a new account ends up on a RomM instance: + +1. **First-user setup** — the person who completes the Setup Wizard. +2. **Invite link** — an admin generates a one-shot link carrying a pre-assigned role. +3. **Public registration** — anyone who reaches `/register` signs themselves up as a Viewer. Off by default. +4. **OIDC auto-provisioning** — first login through your IdP creates a matching account. Covered in [OIDC Setup](oidc/index.md). + +## First-user setup + +When a fresh RomM container starts against an empty database, hitting any page redirects to the **Setup Wizard**. The wizard collects a username, email, and password; the resulting account is **always an Admin**, regardless of any env var. + +To skip the wizard (e.g. when provisioning via automation and you'll create users through the API), set: + +```yaml +environment: + - DISABLE_SETUP_WIZARD=true +``` + +You'll then need to create the first admin via the API or by injecting a row at deploy time — the UI won't offer a setup flow. + +## Invite links + +The recommended way to add users, because it avoids you ever touching their password. + +1. **Administration → Users → Invite.** Pick a role (Viewer, Editor, Admin). +2. RomM generates a single-use URL — copy it and send it to the invitee. +3. When they open it, they pick their own username and password. RomM creates the account with the role you chose and logs them straight in. + +Invite tokens are **single-use** and **time-limited**. Defaults: + +| Setting | Default | Env var | +| --- | --- | --- | +| Expiry | 30 days | `INVITE_TOKEN_DAYS` | + +Expired links return a clear error on the `/register` page — generate a new one from the Users panel. + +!!! tip "Invitations over HTTPS" + Invite URLs include a signed token; they're not useful to anyone without RomM's `ROMM_AUTH_SECRET_KEY`. Still, send them over a trusted channel — once someone has a valid invite URL, they can claim the account. + +## Public self-registration + +Off by default. To let anyone with the `/register` URL create a Viewer account with no invite: + +```yaml +environment: + - ALLOW_PUBLIC_REGISTRATION=true +``` + +When on, the login page grows a "Register" link and `/register` becomes an open endpoint. + +Appropriate for: + +- Instances behind auth at the reverse-proxy layer (Authelia, Cloudflare Access, an IP allowlist). RomM's registration is just paperwork once the proxy has already authenticated the visitor. +- Truly public or group-shared instances where you genuinely want open signup. + +Inappropriate for everything else. **If RomM is exposed to the internet with no upstream auth, leave this off** — it's the single fastest way to fill your DB with spam accounts. + +Anyone who signs up this way is a Viewer. Promote them manually from **Administration → Users → Edit** if needed. + +## Role assignment at sign-up + +| Sign-up method | Role assigned | +| --- | --- | +| First-user Setup Wizard | Admin (always) | +| Invite link | Whatever role the admin picked when generating the link | +| Public registration (`ALLOW_PUBLIC_REGISTRATION=true`) | Viewer | +| OIDC first login | Default Viewer, or mapped from claims via `OIDC_CLAIM_ROLES` | + +Changing a user's role afterwards is a normal admin action — see [Users & Roles](users-and-roles.md). + +## Password reset + +If you've configured an email transport (future work — not part of 5.0 GA), users can self-serve a password reset via **Forgot password?** on the login page. Until then, admins reset passwords manually: **Administration → Users → Edit → New password**. + +See [Authentication](authentication.md) for the session and token side of the auth model. diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 0d5b889b..1cd46bd6 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -1,10 +1,134 @@ --- -status: placeholder -wave: 1 +title: Scanning & Watcher +description: How RomM scans your library, the six scan modes, and the filesystem watcher. --- # Scanning & Watcher -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM keeps its catalogue in sync with your filesystem through three mechanisms: + +1. **Manual scans** you trigger from the Scan page. +2. **Scheduled scans** (default: nightly) run by the task runner. +3. **The filesystem watcher** — live reaction to files landing in or leaving your library. + +All three share the same scan engine and the same set of **scan modes**. + +## Scan modes + +Every scan picks one mode. Modes differ in what they touch — use the most-targeted mode that accomplishes what you want. + +| Mode | What it does | When to use | +| --- | --- | --- | +| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set. Fast. | +| **Quick** | Skips files that already exist in the DB. No metadata refresh. | Default for scheduled runs and the watcher. | +| **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | +| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured). Rare. | +| **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash (pre-4.4) or when you suspect file corruption. | +| **Complete** | Full rescan. Recalculates hashes, re-fetches metadata for everything. | Rarely. Takes a long time. | + +You can further scope a scan to specific **platforms** and specific **metadata providers** — useful when only one provider has changed (e.g. just enabled Hasheous → Unmatched scan, Hasheous selected, on all platforms). + +## Manual scans + +**Scan button in the sidebar.** The Scan page shows: + +- Platform checkboxes (select which to scan; leave empty to scan all). +- Metadata provider toggles (overrides the default priority for this one scan). +- Advanced options: skip hashing (helpful on low-power hosts), target a LaunchBox refresh. +- A live log of everything the scanner is doing. +- Per-platform progress panels with matched / unmatched / missing counts. + +A running scan survives browser refreshes — the log streams over Socket.IO. Multiple admins opening the page see the same scan state. + +## Scheduled scans + +Configured via env vars (full table in the [Scheduled Tasks reference](../reference/scheduled-tasks.md)): + +| Variable | Default | Purpose | +| --- | --- | --- | +| `SCAN_INTERVAL_CRON` | `0 0 * * *` | Cron expression for the scheduled library scan. Runs a **Quick** scan by default. | +| `SCAN_TIMEOUT_HOURS` | `1` | Hard cap. Scans that exceed this are killed and logged. | +| `SCAN_WORKERS` | _auto_ | Concurrent worker processes for scanning. Leave as auto unless you're tuning. | +| `SEVEN_ZIP_TIMEOUT` | — | Per-archive timeout for `.7z` extraction during scan. Raise if scanning huge compressed ROM sets. | + +To disable scheduled scans entirely, either unset the cron or set it to something unreachable (`SCAN_INTERVAL_CRON=0 0 31 2 *`). + +## Filesystem watcher + +The watcher tails your library folder and schedules scans in response to file events — files added, moved, deleted. It's off by default on some deployments; enable with: + +```yaml +environment: + - WATCHER_ENABLED=true + - RESCAN_ON_FILESYSTEM_CHANGE=true + - RESCAN_ON_FILESYSTEM_CHANGE_DELAY=10 # seconds before acting on an event + - WATCH_EXTENSIONS_ONLY=false # true to ignore events on non-ROM extensions +``` + +Behaviour: + +- Watches `/romm/library` (and everything under it) recursively. +- Debounces bursts of events — the delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. +- Batches scans intelligently: many events → a single consolidated scan, not one scan per file. +- Ignores content modifications and metadata-only changes — it cares about files appearing or disappearing, not about `chmod`. +- Skips OS noise (`.DS_Store`, `Thumbs.db`, `.tmp`, etc.). +- If a whole new platform folder appears, switches to a **New Platforms** scan to pick it up cleanly. + +### When **not** to enable the watcher + +- **Slow / high-latency filesystems** (SMB mounts, rclone mounts, anything not local disk). The watcher reacts to every event, and flaky mounts generate a lot. Use scheduled scans instead. +- **Libraries under active write load from other tools** (e.g. a ROM manager constantly tagging files). The watcher will re-scan on every change — at best noisy, at worst a scan loop. + +### Watcher vs scheduled scan + +| | Watcher | Scheduled scan | +| --- | --- | --- | +| Latency | Seconds | Up to your cron interval | +| CPU cost | Only when files change | Constant cadence | +| Works over SMB/NFS | Flakily | Reliably | +| Catches renames | Yes | Yes | +| Survives a container restart | Yes — re-arms on startup | Yes | + +Run both. The watcher handles day-to-day additions; the scheduled scan is a safety net. + +## What gets excluded + +Scans respect the `exclude:` tree in [`config.yml`](../reference/configuration-file.md): + +```yaml +exclude: + platforms: + - steam # skip entire platform folder + roms: + single_file: + extensions: [nfo, txt, bak] # single files with these exts + names: ['*.sample.*'] # Unix glob patterns + multi_file: + names: [extras] # folder names to skip + parts: + names: [thumb.png] # files inside multi-file dirs + extensions: [nfo] +``` + +Full schema in [Configuration File](../reference/configuration-file.md). + +## Region and language preference + +Also in `config.yml`: + +```yaml +scan: + priority: + region: [us, wor, ss, eu, jp] + language: [en, fr] +``` + +When a metadata provider returns multiple regional variants (Japanese cover, US cover, European cover…), RomM picks according to this order. Same for localised titles. + +## Metadata source priority + +Who wins when two providers disagree — covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution). Short version: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. + +## Troubleshooting + +Scans that hang, miss files, or match weirdly: [Scanning Troubleshooting](../troubleshooting/scanning.md). diff --git a/docs/administration/scheduled-tasks.md b/docs/administration/scheduled-tasks.md index 9a133f76..af7be510 100644 --- a/docs/administration/scheduled-tasks.md +++ b/docs/administration/scheduled-tasks.md @@ -1,10 +1,132 @@ --- -status: placeholder -wave: 1 +title: Scheduled Tasks +description: What RomM runs in the background, how to reschedule it, and how to trigger on demand. --- # Scheduled Tasks -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM runs background work through **RQ** (Redis Queue). Tasks fall into four categories: + +- **Scheduled** — cron-driven, run on their own. +- **Watcher** — triggered by filesystem events. +- **Manual** — admin-triggered from the UI or API. +- **Enqueued** — side effects of user actions (a scan started from the Scan page, a metadata refresh on a ROM edit). Not listed here. + +For the lookup-only reference (every task, its cron default, its env var), see [Scheduled Tasks Reference](../reference/scheduled-tasks.md). This page is the narrative — what each one is for, when to worry, and how to tune. + +## The full table + +--8<-- "scheduled-tasks.md" + +## Configuring cadence + +Every scheduled task takes a standard 5-field cron expression: + +```text +minute hour day-of-month month day-of-week +``` + +Examples: + +- `0 3 * * *` — 3 AM daily. +- `0 */6 * * *` — every 6 hours, on the hour. +- `*/30 * * * *` — every 30 minutes. +- `0 2 * * 0` — 2 AM every Sunday. + +Set the env var, restart the container. Alembic runs on every start; the scheduler picks up the new schedule the moment RomM comes back up. + +## Disabling a scheduled task + +Two ways: + +- **The clean way**: set the cron to a time that essentially never fires. `0 0 31 2 *` (Feb 31st) works. +- **The feature-flag way**: some tasks have a dedicated enable toggle — e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false` disables the LaunchBox sync. + +Check the [env var reference](../reference/environment-variables.md) for which toggles exist. + +## Triggering a task manually + +Three paths: + +### From the Administration page + +**Administration → Tasks** shows every task with a Run button. Admins (anyone with `tasks.run` scope) can trigger: + +- Folder Scan (launches the Scan page with the task pre-started). +- Cleanup Missing ROMs. +- Cleanup Orphaned Resources. +- LaunchBox Metadata Sync. +- Switch titleDB Fetch. +- Image Conversion. +- RetroAchievements Sync. +- Netplay Cleanup. +- Push-Pull Device Sync. + +### From the API + +```http +POST /api/tasks/run/{task_name} +Authorization: Bearer +``` + +Full details in the [API Reference](../developers/api-reference.md). Useful for cron runs driven from outside RomM (e.g. an Ansible playbook). + +### Via Redis Queue directly + +Advanced. Connect to Redis, inspect the `rq` queues, enqueue a job manually. Only do this if you know what you're doing and have a reason the API doesn't serve. + +## What each task does + +### Folder Scan + +The nightly library scan. Defaults to **Quick** mode — skips files already in the DB — so it's cheap even on big libraries. See [Scanning & Watcher](scanning-and-watcher.md) for the full scan mechanic. + +### Switch titleDB Fetch + +Downloads an updated copy of the Nintendo Switch title ID database used for matching files with names like `0100000000010000.xci`. Weekly is plenty. + +### LaunchBox Metadata Sync + +Refreshes the local LaunchBox metadata cache used when LaunchBox is enabled as a metadata provider. Nightly at 2 AM by default. Only useful if `LAUNCHBOX_API_ENABLED=true` — disable it otherwise to save a handful of CPU seconds. + +### Image Conversion + +Re-encodes fetched cover art, screenshots, and manuals to WebP for faster serving. Nightly at 3 AM. Idempotent; safe to run more often if you're importing a lot of media. + +### RetroAchievements Sync + +Refreshes per-user RA progression. Nightly at 4 AM. Users see updated achievement counts on their next load. + +### Netplay Cleanup + +Sweeps orphaned netplay sessions — rooms where every participant has disconnected but the metadata lingers. Every 30 minutes by default. + +### Push-Pull Device Sync + +Bidirectional sync with registered devices (handhelds, etc.). Every 15 minutes by default. Disabled implicitly if no devices are registered or if `SSH_PRIVATE_KEY_PATH` isn't configured. See [SSH Sync](ssh-sync.md). + +### Cleanup Missing ROMs (manual) + +Walks the DB, checks each ROM's file still exists on disk, drops entries whose files are gone. Run after major library moves. + +### Cleanup Orphaned Resources (manual) + +Deletes cover images, screenshots, and manuals no longer referenced by any ROM. Safe to run any time; can free tens of GB if you've been churning the library. + +## Monitoring tasks + +- **Live**: Administration → Tasks page shows every task's current status (queued, running, idle, failed). +- **API**: `GET /api/tasks/status` for a JSON summary — wire this to an uptime monitor if you want alerts. +- **Logs**: `docker logs romm` → look for `rq.worker` lines. +- **Heartbeat**: `GET /api/heartbeat` returns overall health plus per-task summary; handy for monitoring dashboards. + +A task that's been "running" for hours is usually a scan that hit `SCAN_TIMEOUT_HOURS` — the log will say so. Tasks that fail leave a stack trace in the container logs; the RQ `failed` queue retains the last few for inspection. + +## Tuning for small hosts + +Defaults assume you've got a reasonable box. On a Pi or NAS with 2 GB of RAM and a single core: + +- Raise the cron intervals (daily → weekly) for the nightlies. +- Set `SCAN_WORKERS=1` to avoid concurrent scan processes. +- Enable the watcher but raise `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` to 60+ seconds — slower reaction, far less churn. +- Disable image conversion if you don't care about WebP (`IMAGE_CONVERSION_INTERVAL_CRON=0 0 31 2 *`). From 222b751754b589ed70cee839a4311f8b9a3fe142 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 16:40:56 +0000 Subject: [PATCH 005/121] docs: draft Wave 1 platform-specific install guides All four Install & Deploy platform pages: - install/unraid.md: merged the two existing Unraid pages into one with the CA-template and Docker-Compose-Manager paths side-by-side, bridge-network prerequisites kept intact - install/synology.md: ported from System-Setup, refreshed for DSM 7.2+, MariaDB 10.7 pinning call-out kept - install/truenas.md: ported from System-Setup with clearer SCALE-only statement and Discord pointer - install/kubernetes.md: NEW. Full manifest set with the mandatory enableServiceLinks:false fix, ingress with proxy-body-size=0, PVC sizes, scaling notes, and a pointer to community Helm charts (no official chart ships in 5.0) Builds clean with --strict. --- docs/install/kubernetes.md | 292 ++++++++++++++++++++++++++++++++++++- docs/install/synology.md | 117 ++++++++++++++- docs/install/truenas.md | 84 ++++++++++- docs/install/unraid.md | 129 +++++++++++++++- 4 files changed, 602 insertions(+), 20 deletions(-) diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index 6d9abd12..deca9543 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -1,10 +1,292 @@ --- -status: placeholder -wave: 1 +title: Kubernetes +description: Deploy RomM on Kubernetes — manifest examples, required quirks, and community Helm charts. --- # Kubernetes -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +There's no official Helm chart for RomM in 5.0. This page walks through a production-grade manifest set and points at the community charts worth looking at. + +!!! note "Community-maintained charts" + If you'd rather not hand-author manifests, there are community Helm charts around — check [ArtifactHub](https://artifacthub.io/packages/search?ts_query_web=romm) and the `#kubernetes` channel in the [RomM Discord](https://discord.gg/P5HtHnhUDH). The RomM team doesn't publish or formally support any chart today — pick one with active maintenance and recent releases. + +## What you need + +- A cluster running Kubernetes 1.27+. +- Persistent storage for the DB, cache, and RomM's assets/resources/config (block or RWX — see below). +- An Ingress controller (nginx-ingress, Traefik, etc.) for external access. +- cert-manager or equivalent for HTTPS. + +## Required quirk: `enableServiceLinks: false` + +Kubernetes injects service addresses as environment variables into every pod — `HOSTNAME_PORT=tcp://:`. nginx inside the RomM image picks these up and tries to bind to the service IP, producing: + +```text +invalid host in "tcp://:8080" of the "listen" directive in +/etc/nginx/conf.d/default.conf:7 +``` + +Fix: disable service-link env vars on the pod. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: romm + namespace: romm +spec: + template: + spec: + enableServiceLinks: false # ← required +``` + +Without this, RomM crashloops on startup. This is the single most common Kubernetes gotcha — if you're here because of the error above, add the flag and move on. + +## Namespace + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: romm +``` + +## Secrets + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: romm-secrets + namespace: romm +type: Opaque +stringData: + ROMM_AUTH_SECRET_KEY: "" + DB_PASSWD: "" + MARIADB_ROOT_PASSWORD: "" + # Metadata providers — fill in only what you've configured: + IGDB_CLIENT_ID: "" + IGDB_CLIENT_SECRET: "" + SCREENSCRAPER_USER: "" + SCREENSCRAPER_PASSWORD: "" + RETROACHIEVEMENTS_API_KEY: "" + STEAMGRIDDB_API_KEY: "" +``` + +## MariaDB + +A single-replica MariaDB is fine for most RomM deployments. Use a StatefulSet so the PVC survives pod restarts. + +```yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: romm-db + namespace: romm +spec: + serviceName: romm-db + replicas: 1 + selector: + matchLabels: { app: romm-db } + template: + metadata: + labels: { app: romm-db } + spec: + enableServiceLinks: false + containers: + - name: mariadb + image: mariadb:11 + env: + - { name: MARIADB_DATABASE, value: romm } + - { name: MARIADB_USER, value: romm-user } + - name: MARIADB_PASSWORD + valueFrom: { secretKeyRef: { name: romm-secrets, key: DB_PASSWD } } + - name: MARIADB_ROOT_PASSWORD + valueFrom: { secretKeyRef: { name: romm-secrets, key: MARIADB_ROOT_PASSWORD } } + ports: + - { name: mysql, containerPort: 3306 } + volumeMounts: + - { name: data, mountPath: /var/lib/mysql } + readinessProbe: + exec: + command: ["healthcheck.sh", "--connect", "--innodb_initialized"] + periodSeconds: 10 + volumeClaimTemplates: + - metadata: { name: data } + spec: + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 10Gi } } +--- +apiVersion: v1 +kind: Service +metadata: + name: romm-db + namespace: romm +spec: + clusterIP: None + ports: + - { name: mysql, port: 3306, targetPort: 3306 } + selector: { app: romm-db } +``` + +## RomM + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: romm + namespace: romm +spec: + replicas: 1 + selector: + matchLabels: { app: romm } + template: + metadata: + labels: { app: romm } + spec: + enableServiceLinks: false # ← required, see top of page + containers: + - name: romm + image: rommapp/romm:5.0.0 + env: + - { name: DB_HOST, value: romm-db } + - { name: DB_NAME, value: romm } + - { name: DB_USER, value: romm-user } + - name: DB_PASSWD + valueFrom: { secretKeyRef: { name: romm-secrets, key: DB_PASSWD } } + - name: ROMM_AUTH_SECRET_KEY + valueFrom: { secretKeyRef: { name: romm-secrets, key: ROMM_AUTH_SECRET_KEY } } + - { name: ROMM_BASE_URL, value: "https://romm.example.com" } + - { name: HASHEOUS_API_ENABLED, value: "true" } + # ... other metadata provider vars from the secret + envFrom: + - secretRef: { name: romm-secrets } + ports: + - { name: http, containerPort: 8080 } + volumeMounts: + - { name: library, mountPath: /romm/library, readOnly: true } + - { name: assets, mountPath: /romm/assets } + - { name: resources, mountPath: /romm/resources } + - { name: config, mountPath: /romm/config } + - { name: redis-data, mountPath: /redis-data } + readinessProbe: + httpGet: { path: /api/heartbeat, port: http } + initialDelaySeconds: 30 + periodSeconds: 10 + volumes: + - name: library + persistentVolumeClaim: { claimName: romm-library } + - name: assets + persistentVolumeClaim: { claimName: romm-assets } + - name: resources + persistentVolumeClaim: { claimName: romm-resources } + - name: config + persistentVolumeClaim: { claimName: romm-config } + - name: redis-data + persistentVolumeClaim: { claimName: romm-redis-data } +--- +apiVersion: v1 +kind: Service +metadata: + name: romm + namespace: romm +spec: + ports: + - { name: http, port: 80, targetPort: 8080 } + selector: { app: romm } +``` + +## Persistent Volume Claims + +One PVC per volume. Sizes are rough; tune to your library. + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: romm-library, namespace: romm } +spec: + accessModes: [ReadOnlyMany] # your ROM store, typically an RWX mount + resources: { requests: { storage: 500Gi } } +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: romm-assets, namespace: romm } +spec: + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 20Gi } } +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: romm-resources, namespace: romm } +spec: + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 50Gi } } +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: romm-config, namespace: romm } +spec: + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 1Gi } } +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: romm-redis-data, namespace: romm } +spec: + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 5Gi } } +``` + +For shared library access (multiple RomM replicas, or CI jobs that bulk-import), use RWX on the library PVC (NFS, CephFS, Longhorn-RWX). Assets must also be RWX if you ever run more than one RomM replica. + +## Ingress + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: romm + namespace: romm + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + # Crucial: allow large uploads + websockets + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" +spec: + ingressClassName: nginx + tls: + - hosts: [romm.example.com] + secretName: romm-tls + rules: + - host: romm.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: romm + port: { number: 80 } +``` + +`proxy-body-size: "0"` is important — the default is 1 MB and will reject every ROM upload with HTTP 413. + +## Scaling notes + +- **One RomM replica** is the simple path. The scan runs as a single worker, which prefers a single replica. +- **Multiple replicas** work, but you need RWX for `/romm/assets` and `/romm/resources` and an external Redis (set `REDIS_HOST` to a shared service). See [Redis or Valkey](redis-or-valkey.md). +- **No HPA** (horizontal pod autoscaler) on RomM — CPU spikes during scans are normal and not a scaling signal. + +## Updating + +```sh +kubectl set image -n romm deployment/romm romm=rommapp/romm:5.0.1 +``` + +Alembic runs on startup; migrations happen automatically. Before major-version upgrades, read the release notes and take a backup (see [Backup & Restore](backup-and-restore.md)). + +## Troubleshooting + +Common Kubernetes-specific issues: [Kubernetes Troubleshooting](../troubleshooting/kubernetes.md). diff --git a/docs/install/synology.md b/docs/install/synology.md index ca418eb9..5581d072 100644 --- a/docs/install/synology.md +++ b/docs/install/synology.md @@ -1,10 +1,117 @@ --- -status: placeholder -wave: 1 +title: Synology +description: Install RomM on a Synology NAS via Container Manager / Docker. --- # Synology -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +## Looking for an opinionated guide? + +[Marius Bogdan Lixandru](https://mariushosting.com/) maintains excellent Synology-focused guides with either MariaDB or Postgres: + +- [How to Install RomM on Your Synology NAS (MariaDB)](https://mariushosting.com/how-to-install-romm-on-your-synology-nas/) +- [How to Install RomM With PostgreSQL on Your Synology NAS](https://mariushosting.com/how-to-install-romm-with-postgresql-on-your-synology-nas/) + +Follow those if they match your setup. The walkthrough below is the fallback for everything else. + +## Prerequisites + +- A Synology NAS with Container Manager (DSM 7.2+) or the legacy Docker package. +- SSH or a task that lets you run shell commands on the NAS. +- Basic comfort with Docker Compose. + +## 1. Create folders + +RomM wants its data split across a few well-known paths. Do this once, via SSH: + +### ROM library + +```bash +mkdir -p /volume1/data/media/games/library/roms +mkdir -p /volume1/data/media/games/library/bios +``` + +The platform folder names inside `roms/` have to match RomM's conventions — see [Folder Structure](../getting-started/folder-structure.md). + +### User uploads + config + +```bash +mkdir -p /volume1/data/media/games/assets +mkdir -p /volume1/data/media/games/config +``` + +### Docker volumes for RomM itself + +```bash +mkdir -p /volume1/docker/romm-project/ +mkdir -p /volume1/docker/romm/resources +mkdir -p /volume1/docker/romm/redis-data +mkdir -p /volume1/docker/mariadb-romm +``` + +## 2. Create a bridge network + +RomM and MariaDB need to reach each other by container name. Create a Docker bridge named `rommbridge` — [guide here](https://drfrankenstein.co.uk/step-3-setting-up-a-docker-bridge-network-in-container-manager/). + +## 3. Generate the auth secret + +```bash +openssl rand -hex 32 +# -> 03a054b6ca27e0107c5eed552ea66bacd9f3a2a8a91e7595cd462a593f9ecd09 +``` + +Keep the output — it becomes `ROMM_AUTH_SECRET_KEY` in your compose file. Don't lose it; rotating invalidates every session and invite link. + +## 4. Set up metadata provider credentials + +Recommended before the first scan. Full walkthrough in [Metadata Providers](../administration/metadata-providers.md). + +## 5. Docker Compose + +!!! info "MariaDB 10.7 note" + This guide pins MariaDB to **10.7** for stability on older DSM versions. MariaDB 11 works on DSM 7.2+ — bump the image tag if you like. + +The Synology-flavoured compose file — MariaDB on port `3309` externally (to avoid colliding with Synology's built-in MariaDB), UID/GID customisation, simplified healthcheck: + +???+ example "docker-compose.yml" + ``` yaml + --8<-- "synology.docker-compose.yml" + ``` + +Replace placeholder UIDs, GIDs, passwords, API keys, and `ROMM_AUTH_SECRET_KEY` with your own before starting. + +## 6. Launch + +From the directory holding your compose file: + +```bash +sudo docker compose up -d +``` + +**Be patient** — the first start takes a few minutes while MariaDB initialises, RomM runs migrations, and resources get seeded. Tail the logs: + +```bash +sudo docker compose logs -f +``` + +Once RomM reports it's listening, open `http://:7676` in a browser. The Setup Wizard walks you through creating the first admin. + +## Notes + +- **Permissions**: make sure the UID/GID in your compose file has read-write on every host path you mounted. Synology's default `docker` user is often `1024:100`; the `apps` user is `568`. Pick one and be consistent. +- **HTTPS**: put Synology's built-in reverse proxy (Control Panel → Login Portal → Advanced → Reverse Proxy) in front of RomM, or use the [Reverse Proxy](reverse-proxy.md) recipes. +- **Back up `/volume1/docker/romm` and your DB volume** before upgrading RomM versions. See [Backup & Restore](backup-and-restore.md). + +## Troubleshooting + +Common Synology gotchas: + +- **"Page not found" on first open** — DSM hit RomM before it finished first-run init. Wait for `docker compose logs -f` to calm down. +- **Database connection errors** — check the MariaDB container is healthy (`docker ps` → status `healthy`), and that RomM's `DB_HOST` matches the MariaDB service name in compose. +- **Permission errors on assets/resources folders** — verify the UID/GID in the compose matches the owner of those host paths on the NAS (`ls -la /volume1/data/media/games/`). + +Synology-specific problems that come up often: [Synology Troubleshooting](../troubleshooting/synology.md). + +## Contributing + +Originally adapted from [ChopFoo's guide](https://github.com/rommapp/docs). Suggestions welcome via PR. diff --git a/docs/install/truenas.md b/docs/install/truenas.md index 69bd03a7..044ac211 100644 --- a/docs/install/truenas.md +++ b/docs/install/truenas.md @@ -1,10 +1,84 @@ --- -status: placeholder -wave: 1 +title: TrueNAS +description: Install RomM on TrueNAS SCALE via the App Catalog or YAML. --- # TrueNAS -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported — its FreeBSD jail system doesn't run Docker images. + +## Prerequisites + +- A running TrueNAS SCALE installation. +- Your ROM library arranged in the expected [folder structure](../getting-started/folder-structure.md). +- A TrueNAS user/group with a UID/GID that can own the dataset paths you'll mount into RomM. + +## Option A — App Catalog (recommended) + +### 1. Open the RomM app + +**Apps** (left nav) → **Discover Apps** → search `RomM` → **Install**. + +![RomM app](../resources/truenas/appstore.png) + +### 2. Fill in the install form + +Step through the installation UI. You'll be asked for the same set of env vars as [Quick Start](../getting-started/quick-start.md) — most defaults work. Things to look at: + +- **Database credentials** — TrueNAS will offer to provision MariaDB for you; just pick a strong password. +- **`ROMM_AUTH_SECRET_KEY`** — generate via `openssl rand -hex 32` on any Linux box and paste the output. +- **Metadata provider credentials** — fill in whatever you've registered for. See [Metadata Providers](../administration/metadata-providers.md). +- **Storage configurations** — point the **Library** and **Assets** volumes at datasets you control. Make sure the UID/GID defined in the app config (default: `568` — the `apps` user) has ACL access to those datasets. + +![RomM Library Example](../resources/truenas/app-config.png) + +### 3. Install + +Save. TrueNAS provisions the container + DB + Redis, runs migrations, and exposes the web UI on the port you configured. If it won't boot, jump to [Troubleshooting](#troubleshooting). + +## Option B — Install via YAML + +Use this path when the App Catalog has a bug, or when you want more flexibility than the install UI exposes. + +### 1. Open the YAML install + +**Apps** → **Discover Apps** → **Install via YAML**. + +![Install via YAML](../resources/truenas/install-via-yaml.png) + +### 2. Paste the compose file + +Fill in the empty values with credentials you created in [Quick Start](../getting-started/quick-start.md). + +???+ example "docker-compose.yml" + ``` yaml + --8<-- "truenas.docker-compose.yml" + ``` + +### 3. Install + +Save. Same troubleshooting applies — see below. + +## Troubleshooting + +### Generic + +- Check you've filled in UIDs, GIDs, passwords, and any required API keys. +- Make sure the TrueNAS dataset permissions allow the chosen UID/GID to read/write. +- Watch the app's terminal / logs during startup for clues. + +### Permission errors inside the container + +If you're seeing permission errors on paths *inside* the RomM container (not on TrueNAS datasets), try temporarily running the container as root (`user: 0`) to unblock yourself, fix the offending permissions via shell, and switch back to a non-root user. + +In at least one reported setup, creating a user/group in TrueNAS with UID/GID `1000:1000` and the auxiliary `apps` group was needed to get RomM talking to its embedded Redis cleanly. + +### Other issues + +- [Scanning Troubleshooting](../troubleshooting/scanning.md) for matching / ingest problems. +- [Authentication Troubleshooting](../troubleshooting/authentication.md) for login issues. +- The RomM [Discord](https://discord.gg/P5HtHnhUDH) has a `#truenas` channel with active community troubleshooting. + +## Contributing + +Suggestions welcome — PRs against [rommapp/docs](https://github.com/rommapp/docs). diff --git a/docs/install/unraid.md b/docs/install/unraid.md index aa20e124..f1be2b18 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -1,10 +1,129 @@ --- -status: placeholder -wave: 1 +title: Unraid +description: Install RomM on Unraid via the Community Apps template or the Docker Compose Manager plugin. --- # Unraid -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Two supported install paths on Unraid. Pick one: + +- **[Community Apps template](#community-apps-template)** — simplest. Install RomM and MariaDB as separate CA templates. Good for users who already manage containers one-at-a-time. +- **[Docker Compose Manager](#docker-compose-manager)** — closer to the upstream reference setup. Drops the standard `docker-compose.yml` in and uses the Compose plugin to manage it. Recommended if you're comfortable editing Compose files and want parity with other deployments. + +Both end up with the same running stack. + +!!! warning "Back up `appdata` before updates" + Tearing down the RomM container wipes its resources directory (covers, screenshots, cached metadata). Mount `appdata` on a safe path or [back it up](backup-and-restore.md) before every upgrade. + +--- + +## Community Apps template + +### Prerequisites + +- [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) installed. +- A custom Docker bridge network so RomM and MariaDB can talk to each other by container name. Skip this and you'll hit DNS issues that look like everything else. + + ```sh + docker network create romm + docker network ls # confirm `romm` is listed + ``` + + ![console output](https://github.com/user-attachments/assets/bac31998-1911-4085-b115-8dd93d519b8b) + +### 1. Install MariaDB + +From **Apps** → search `mariadb`. Only the [official `mariadb`](https://hub.docker.com/_/mariadb) and [linuxserver/docker-mariadb](https://github.com/linuxserver/docker-mariadb/pkgs/container/mariadb) templates are supported — **prefer the official one**. + +![community apps search results for MariaDB](https://github.com/user-attachments/assets/76f4b6ef-5b63-454f-9357-d2920b9afd0e) + +Fill in the env vars — names and sensible defaults live in the [reference `docker-compose.yml`](docker-compose.md). Set the network to **Custom: romm**. + +![MariaDB environment variables](https://github.com/user-attachments/assets/a11906c5-25b2-46f1-906b-451a9ee39dca) + +!!! warning "Network type" + MariaDB's network type **must** be set to `Custom: romm`. Otherwise RomM can't resolve its hostname. + +### 2. Install RomM + +From **Apps** → search `romm` → install the app labelled **OFFICIAL** (maintained by the RomM team, always current). + +![RomM official app](https://github.com/user-attachments/assets/57c4d47a-8604-4e8d-b05a-84dd68dda124) + +Fill in env vars, ports, and paths per the [reference compose](docker-compose.md). Again, network type → `Custom: romm`. + +![RomM docker tab](https://github.com/user-attachments/assets/4c4210c2-ed00-4790-a945-65cbe33620b0) + +### 3. Done + +Apply, head back to the **Docker** tab, and you should see both containers running. Access RomM at the IP:port highlighted below. + +![RomM and MariaDB running](https://github.com/user-attachments/assets/cba26de1-d2c9-4fff-88d8-bc7701f0dd88) + +--- + +## Docker Compose Manager + +### Prerequisites + +- [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/). +- [Docker Compose Manager plugin](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/) from CA. + + ![Docker Compose Addon](../resources/unraid/docker-compose.png) + +After installing, a **Compose** section appears under the Docker Containers list on the Docker tab. + +![Docker Compose Section](../resources/unraid/unraid-start.png) + +### 1. Add the stack + +**Add New Stack** → name it **RomM** → OK. + +Click the gear icon → **Edit Stack** → **Edit Compose**. + +![Edit Stack](../resources/unraid/edit-stack.png) + +Paste the [reference `docker-compose.yml`](docker-compose.md) and fill in your env vars (API keys, MariaDB creds, metadata providers). You can keep secrets in a separate `.env` file — edit the environment file via the gear icon. + +![Edit Compose](../resources/unraid/unraid-compose.png) + +![Edit Env](../resources/unraid/romm-env.png) + +Save after each edit. + +!!! warning "Folder structure" + Make sure your library matches one of the [supported folder layouts](../getting-started/folder-structure.md) before scanning — Unraid users often forget this step. + +### 2. Bring it up + +Click **Compose Up**. + +![Compose Up Working](../resources/unraid/docker-compose-loading.png) + +Copy `IP:Port` from the RomM container and open it in a browser — the first-run Setup Wizard should appear. + +![Compose Up](../resources/unraid/docker-compose-up.png) + +![Romm Setup](../resources/unraid/unraid-success.png) + +--- + +## Video walkthroughs + +Community-made, still relevant for general Unraid/RomM debugging even if specific UI screens have drifted. + +[DemonWarriorTech](https://www.youtube.com/@DemonWarriorTech) — [How to Install RomM on Unraid (Beginner Friendly)](https://www.youtube.com/watch?v=Oo5obHNy2iw): + +[![RomM on Unraid — DemonWarriorTech](https://img.youtube.com/vi/Oo5obHNy2iw/0.jpg)](https://www.youtube.com/watch?v=Oo5obHNy2iw) + +[AlienTech42](https://www.youtube.com/@AlienTech42) — [older install + debugging walkthrough](https://www.youtube.com/watch?v=ls5YcsFdwLQ). Install steps are out of date, but the general-setup and debugging portions still hold. + +[![RomM on Unraid — AlienTech42](https://img.youtube.com/vi/ls5YcsFdwLQ/0.jpg)](https://www.youtube.com/watch?v=ls5YcsFdwLQ) + +## Unraid community support + +Dedicated [Unraid forums support thread](https://forums.unraid.net/topic/149738-support-eurotimmy-romm-rom-manager-by-zurdi15/) — good for Unraid-specific questions that aren't in the RomM Discord's Unraid channel. + +## Shout-outs + +Thanks to @Smurre95 and @sfumat0 for documenting this process and getting RomM listed in Community Apps. From be2b1a11275308161cbc3d4f2e6f058bebaac166 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 16:46:45 +0000 Subject: [PATCH 006/121] docs: port Wave 1 OIDC provider guides (all 5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All five OIDC provider walkthroughs now live under administration/oidc/, consistent in structure: prereqs → provider-side config → RomM env vars → email matching → test → optional role mapping. - administration/oidc/authelia.md: claims policy + client config - administration/oidc/authentik.md: 2025.10 email_verified property mapping, provider + application setup - administration/oidc/keycloak.md: previously orphaned; now linked from Navigation.md and the OIDC hub. Uses DISABLE_USERPASS_LOGIN (replacing the old doc's PASSWORD_AUTH_ENABLED which doesn't exist in the env template) - administration/oidc/pocketid.md: passkey-only flow - administration/oidc/zitadel.md: project/app setup, "User Info inside ID Token" fix for the "Email is missing from token" error All cross-link to the hub's role-mapping section and to troubleshooting/authentication.md. Builds clean with --strict. --- docs/administration/oidc/authelia.md | 99 ++++++++++++++++++++++-- docs/administration/oidc/authentik.md | 107 ++++++++++++++++++++++++-- docs/administration/oidc/keycloak.md | 89 +++++++++++++++++++-- docs/administration/oidc/pocketid.md | 54 +++++++++++-- docs/administration/oidc/zitadel.md | 79 +++++++++++++++++-- 5 files changed, 403 insertions(+), 25 deletions(-) diff --git a/docs/administration/oidc/authelia.md b/docs/administration/oidc/authelia.md index 50c60085..e3c857de 100644 --- a/docs/administration/oidc/authelia.md +++ b/docs/administration/oidc/authelia.md @@ -1,10 +1,99 @@ --- -status: placeholder -wave: 1 +title: OIDC with Authelia +description: Wire RomM's SSO to Authelia — claims policy, client registration, RomM env vars. --- # OIDC with Authelia -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +[Authelia](https://www.authelia.com/) is a lightweight open-source authentication and authorisation server with two-factor auth and SSO. Good fit for homelabs that already proxy through a reverse proxy. + +Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. + +## 1. Prerequisites + +Authelia installed and running, with its OIDC provider enabled. Upstream guides: + +- [Authelia getting started](https://www.authelia.com/integration/prologue/get-started/) +- [OIDC provider configuration](https://www.authelia.com/configuration/identity-providers/openid-connect/provider/) + +## 2. Add a claims policy + +In Authelia's `configuration.yml` under `identity_providers.oidc.claims_policies`, add a policy that emits the claims RomM needs (name it whatever you like): + +```yaml +identity_providers: + oidc: + claims_policies: + with_email: + id_token: + - email + - email_verified + - groups + - alt_emails + - preferred_username + - name +``` + +Background on why this is needed: [Authelia claims parameter restoration](https://www.authelia.com/integration/openid-connect/openid-connect-1.0-claims/#restore-functionality-prior-to-claims-parameter). + +## 3. Register the RomM client + +Under `identity_providers.oidc.clients`, add: + +```yaml +identity_providers: + oidc: + clients: + - client_id: "" # see note below + client_name: "RomM" + client_secret: "$pbkdf2-sha512$" # see note below + public: false + authorization_policy: "two_factor" # or one_factor + grant_types: + - authorization_code + redirect_uris: + - "https://romm.example.com/api/oauth/openid" + claims_policy: "with_email" # must match the policy name above + scopes: + - openid + - email + - profile + - groups + userinfo_signed_response_alg: "none" + token_endpoint_auth_method: "client_secret_basic" +``` + +Generating IDs and secrets: see [Authelia's FAQ](https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret). Full client schema: [Authelia clients reference](https://www.authelia.com/configuration/identity-providers/openid-connect/clients/). + +## 4. Configure RomM + +In the `romm` service environment: + +```yaml +environment: + - OIDC_ENABLED=true + - OIDC_PROVIDER=authelia + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com + - ROMM_BASE_URL=https://romm.example.com +``` + +`OIDC_REDIRECT_URI` must match what you put in `redirect_uris` exactly — scheme, host, path, no trailing slash. + +For role mapping from Authelia groups, see [OIDC Setup → Role mapping](index.md#role-mapping-50). + +## 5. Set your email on RomM + +In RomM → **Profile** → set your email to exactly the same address Authelia has for you. RomM matches OIDC users to existing accounts by email. + +![Set email](../../resources/authelia/1-user-profile.png) + +## 6. Test + +Restart RomM and open `/login`. There should be a **Login with OIDC** button alongside the username/password form. Click it → you're bounced to Authelia → authenticate → you're bounced back and signed into RomM. + +![Login with OIDC](../../resources/authelia/2-romm-login.png) + +If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md index 4cbf0c3e..aadcd222 100644 --- a/docs/administration/oidc/authentik.md +++ b/docs/administration/oidc/authentik.md @@ -1,10 +1,107 @@ --- -status: placeholder -wave: 1 +title: OIDC with Authentik +description: Wire RomM's SSO to Authentik — property mapping for email_verified, provider, application, RomM env vars. --- # OIDC with Authentik -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +[Authentik](https://goauthentik.io/) is a full-featured open-source IdP with MFA, flows, and a sizeable audit/admin surface. Good fit for users who want more than Authelia offers. + +Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. + +## 1. Prerequisites + +Authentik installed and running. Upstream: [Docker Compose install guide](https://docs.goauthentik.io/docs/install-config/install/docker-compose). + +Log in as admin and open **Admin Interface**. + +![Authentik user dashboard](../../resources/authentik/1-user-dashboard.png) + +## 2. Create a property mapping (Authentik 2025.10+) + +!!! important "Authentik 2025.10 breaking change" + In version 2025.10, Authentik changed the default of `email_verified` from `true` to `false`. RomM requires a verified email, so without this property mapping, authentication silently fails. + +In **Customization → Property Mappings → Create → Scope Mapping**: + +- **Name**: `RomM Email Verification` +- **Scope name**: `email` +- **Expression**: + + ```py + return { + "email": user.email, + "email_verified": True, + } + ``` + +![Property Mapping](../../resources/authentik/propperty-mapping.png) + +Click **Create**. Upstream reference: [Authentik scope mappings](https://version-2025-10.goauthentik.io/add-secure-apps/providers/property-mappings/#scope-mappings-with-oauth2). + +## 3. Create a provider + +**Admin → Providers → Create**. + +![Create a new provider](../../resources/authentik/2-create-provider.png) + +Choose **OAuth2/OpenID Provider**. + +![Select OAuth2 provider](../../resources/authentik/3-new-provider.png) + +Configure: + +- **Name**: `RomM OIDC Provider` +- **Authorization flow**: implicit consent +- **Redirect URIs**: `https://romm.example.com/api/oauth/openid` + +Copy the generated **Client ID** and **Client Secret** — you'll use them as `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` on the RomM side. + +![Provider settings](../../resources/authentik/4-provider-secrets.png) + +Click **Create**. + +## 4. Register the application + +**Admin → Applications → Create**. + +![Applications](../../resources/authentik/5-applications.png) + +- **Name**: `RomM` +- **Slug**: `romm` +- **Provider**: the `RomM OIDC Provider` you just made. + +![New application](../../resources/authentik/6-new-application.png) + +Click **Create**. + +## 5. Configure RomM + +```yaml +environment: + - OIDC_ENABLED=true + - OIDC_PROVIDER=authentik + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com/application/o/romm + - ROMM_BASE_URL=https://romm.example.com +``` + +Note `OIDC_SERVER_APPLICATION_URL` points at the per-application URL (`/application/o/`) — not the Authentik root. + +For role mapping from Authentik groups, see [OIDC Setup → Role mapping](index.md#role-mapping-50). + +## 6. Set your email on RomM + +In RomM → **Profile** → set your email to exactly the same address Authentik has for you. + +![Set email](../../resources/authentik/7-user-profile.png) + +## 7. Test + +Restart RomM and open `/login`. Click the **Login with OIDC** button — you're redirected to Authentik, authenticate, and come back signed into RomM. + +![Login with OIDC](../../resources/authentik/8-romm-login.png) + +If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). diff --git a/docs/administration/oidc/keycloak.md b/docs/administration/oidc/keycloak.md index 1e255e14..1f0f1cc0 100644 --- a/docs/administration/oidc/keycloak.md +++ b/docs/administration/oidc/keycloak.md @@ -1,10 +1,89 @@ --- -status: placeholder -wave: 1 +title: OIDC with Keycloak +description: Wire RomM's SSO to Keycloak — realm, client, RomM env vars, optional role mapping. --- # OIDC with Keycloak -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +[Keycloak](https://www.keycloak.org/) is the heavyweight open-source IAM standard. Feature-complete, well-documented, widely deployed — the default choice when your SSO needs are serious. + +Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. + +## 1. Prerequisites + +Keycloak installed and running. Upstream: [Keycloak getting started](https://www.keycloak.org/getting-started). + +Log into the **Admin Console** and either create a new realm for RomM or reuse an existing one. + +## 2. Add a client + +In the Admin Console, select your realm → **Clients** → **Create client**. + +1. **Client type**: `OpenID Connect`. +2. **Client ID**: `romm` (or anything unique). Click **Next**. +3. On the capability page: + - Enable **Client authentication**. + - Leave only **Standard flow** enabled. + - Click **Next**. +4. Set URLs: + - **Root URL**: `https://romm.example.com` + - **Valid Redirect URIs**: `https://romm.example.com/api/oauth/openid` + - **Web origins**: `https://romm.example.com` +5. Save. Go to **Credentials** tab and copy the **Client Secret**. + +## 3. Configure RomM + +```yaml +environment: + - OIDC_ENABLED=true + - OIDC_PROVIDER=keycloak + - OIDC_CLIENT_ID=romm + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://keycloak.example.com/realms/ + - ROMM_BASE_URL=https://romm.example.com +``` + +`OIDC_SERVER_APPLICATION_URL` must include the realm — `.../realms/` — not just the Keycloak root. + +## 4. Set email on RomM + verify in Keycloak + +In RomM → **Profile** → set your email to the same address Keycloak has for you. + +On the Keycloak side, **Admin Console → Users**: mark each RomM user's email as **verified**. Users with unverified emails will be rejected on login. + +## 5. Test + +Restart RomM and open `/login`. Click **Login with Keycloak** — you're redirected to Keycloak, authenticate, and bounce back signed into RomM. + +If a user already exists in RomM with a matching email, they're signed into that account. Otherwise RomM creates a new account with Viewer permissions. + +If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). + +## 6. (Optional) Disable local password logins + +Force users through Keycloak: + +```yaml +environment: + - DISABLE_USERPASS_LOGIN=true +``` + +!!! warning + Before flipping this, confirm at least one Admin account can sign in via OIDC. Otherwise a broken OIDC flow locks you out. + +## 7. (Optional) Role mapping + +By default, new OIDC users come in as Viewers. To map Keycloak roles/groups to RomM roles: + +```yaml +environment: + - OIDC_CLAIM_ROLES=groups # or realm_access.roles, depending on your token + - OIDC_ROLE_VIEWER=romm-viewer + - OIDC_ROLE_EDITOR=romm-editor + - OIDC_ROLE_ADMIN=romm-admin +``` + +Configure Keycloak's client to include the role/group claim in the ID token (usually via a **Group Membership** or **Realm Role** client scope mapper). Values in the claim are compared against the `OIDC_ROLE_*` env vars on every login, so demoting in Keycloak takes effect on the user's next sign-in. + +See [OIDC Setup → Role mapping](index.md#role-mapping-50) for the generic version. diff --git a/docs/administration/oidc/pocketid.md b/docs/administration/oidc/pocketid.md index b3ffacb0..d0d7e426 100644 --- a/docs/administration/oidc/pocketid.md +++ b/docs/administration/oidc/pocketid.md @@ -1,10 +1,54 @@ --- -status: placeholder -wave: 1 +title: OIDC with PocketID +description: Wire RomM's SSO to PocketID for passkey-only login. --- # OIDC with PocketID -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +[PocketID](https://github.com/stonith404/pocket-id) is a minimalist OIDC provider that **only** supports passkey authentication — no passwords. Good fit when you want passwordless login end-to-end without the complexity of Keycloak or Authentik. + +Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. + +## 1. Prerequisites + +PocketID installed, running, and your admin passkey already registered. Upstream setup: [PocketID setup guide](https://github.com/stonith404/pocket-id#setup). + +## 2. Add the client + +In PocketID admin: + +1. **Application Configuration** — make sure **Emails Verified** is ticked. RomM requires verified emails. +2. Go to **OIDC Client** → **Add OIDC Client**. +3. Fill in: + - **Name**: `RomM` + - **Callback URLs**: `https://romm.example.com/api/oauth/openid` +4. **Save**. Stay on this page — the client secret only displays **once**. Copy both the Client ID and Client Secret now. + +## 3. Configure RomM + +```yaml +environment: + - OIDC_ENABLED=true + - OIDC_PROVIDER=pocket-id + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://id.example.com + - ROMM_BASE_URL=https://romm.example.com +``` + +`OIDC_SERVER_APPLICATION_URL` is the root URL of your PocketID instance. + +## 4. Set your email on RomM + +RomM → **Profile** → set your email to exactly the same address PocketID has for you. + +![Set email](../../resources/authelia/1-user-profile.png) + +## 5. Test + +Restart RomM and open `/login`. Click **Login with OIDC** — you're redirected to PocketID, tap your passkey, and bounce back signed into RomM. + +![Login with OIDC](../../resources/pocketid/2-romm-login.png) + +If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). diff --git a/docs/administration/oidc/zitadel.md b/docs/administration/oidc/zitadel.md index 3d69080e..6b124ad7 100644 --- a/docs/administration/oidc/zitadel.md +++ b/docs/administration/oidc/zitadel.md @@ -1,10 +1,79 @@ --- -status: placeholder -wave: 1 +title: OIDC with Zitadel +description: Wire RomM's SSO to Zitadel — project, application, user info inside ID token, RomM env vars. --- # OIDC with Zitadel -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +[Zitadel](https://zitadel.com/) is an enterprise-grade open-source IAM platform supporting OAuth2, OIDC, SAML, and passwordless. Good fit when you want an enterprise-ish IdP without running Keycloak. + +Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. + +## 1. Prerequisites + +Zitadel installed and running. Upstream: [self-hosted deployment docs](https://zitadel.com/docs/self-hosting/deploy/overview). Change the default organization password before you go further. + +## 2. Create a project + +Create a new project (e.g. `RomM`). This holds the client and its auth settings. + +On the project's **General** tab, the toggles mean: + +- **Assert Roles on Authentication** — not useful today; RomM 5.0 reads roles via `OIDC_CLAIM_ROLES` regardless. Leave off unless you're setting up role mapping. +- **Check authorization on Authentication** — recommended. If off, anyone who can register in Zitadel can sign into RomM (as Viewer). Turn this on if Zitadel registration is open. +- **Check for Project on Authentication** — only matters if you're separating users by Zitadel organization. Skip for a single RomM instance. + +### 2.5 (Optional) Grant users to the project + +If you enabled **Check authorization on Authentication**: + +1. **Authorization** tab → **New**. +2. Select user(s) → **Continue**. +3. "No role has been created yet" is fine — just **Save**. The user appears in the authorization list with no roles. + +## 3. Create the application + +On the project's **General** tab, under **Applications**, click **New**. Tick **I'm a pro. Skip this wizard** for the fast path. + +- **Name**: `RomM` +- **Application Type**: `Web` +- **Grant Types**: `Authorization Code` +- **Response Types**: `Code` +- **Authentication Method**: `Basic` +- **Redirect URIs**: `https://romm.example.com/api/oauth/openid` +- **Post Logout URIs**: `https://romm.example.com/` + +Click **Create**. The **client secret is shown once** — copy it now. + +## 4. Enable claims in the ID Token + +Without this, RomM throws "Email is missing from token" on login. + +Open the application's **Token Settings** tab → tick **User Info inside ID Token** → **Save**. + +## 5. Configure RomM + +```yaml +environment: + - OIDC_ENABLED=true + - OIDC_PROVIDER=zitadel + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://zitadel.example.com + - ROMM_BASE_URL=https://romm.example.com +``` + +Zitadel's OIDC discovery URL is at `/.well-known/openid-configuration` — handy for debugging. + +For role mapping from Zitadel, see [OIDC Setup → Role mapping](index.md#role-mapping-50). + +## 6. Set email on RomM + Zitadel + +In RomM → **Profile** → set your email to exactly the same address your Zitadel user has. + +## 7. Test + +Restart RomM and open `/login`. Click **Login with Zitadel** — you're redirected, authenticate, and bounce back signed into RomM. + +If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). From ee1b5adf42b6a4b1cb6c4c9968f8979749c227d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 16:52:17 +0000 Subject: [PATCH 007/121] docs: draft Wave 1 getting-started bucket (4 pages) Foundation pages every other section cross-links into: - getting-started/folder-structure.md: ported from existing page. Both Structure A (recommended) / Structure B (fallback) trees, multi-file game conventions, filename tagging, region+language codes. Cross-links refreshed for the new IA. - getting-started/first-scan.md: NEW walkthrough for populating a fresh install. Covers pre-flight checks, live scan UI, what "matched" vs "unmatched" means, scan-mode hints, and what to do when the scan finishes. - getting-started/concepts.md: NEW canonical glossary. 18 short entries (Library, Platform, ROM, Collection variants, Asset, Firmware, Role/Scope, Client API Token, Device, Play Session, Task, Console Mode, Kiosk, EmulatorJS/Ruffle, Netplay, etc.) with out-links to their full pages. Clarifies the resources/ vs assets/ dir name-collision. - getting-started/what-is-new-in-5.md: NEW release elevator pitch for end-users / operators / ecosystem devs, plus a breaking-changes-at-a-glance table. Cross-links everything interesting in the overhaul. Builds clean with --strict. --- docs/getting-started/concepts.md | 96 ++++++++- docs/getting-started/first-scan.md | 86 +++++++- docs/getting-started/folder-structure.md | 250 ++++++++++++++++++++++- docs/getting-started/what-is-new-in-5.md | 137 ++++++++++++- 4 files changed, 549 insertions(+), 20 deletions(-) diff --git a/docs/getting-started/concepts.md b/docs/getting-started/concepts.md index efa1ec03..4b371135 100644 --- a/docs/getting-started/concepts.md +++ b/docs/getting-started/concepts.md @@ -1,10 +1,96 @@ --- -status: placeholder -wave: 1 +title: Core Concepts +description: The vocabulary that every RomM page assumes you know. --- # Core Concepts -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Everything in RomM is made of a dozen or so concepts that keep recurring across the docs, the API, and the UI. Here they are, short and flat. Deep dives link out. + +## Library + +Your ROM files on disk. RomM reads the library (usually read-only) and builds its catalogue from what it finds. The library lives at the path you mount into the container as `/romm/library`. See [Folder Structure](folder-structure.md). + +## Platform + +A gaming system — SNES, PlayStation, Game Boy Advance, DOS, etc. RomM ships with ~400 supported platforms (see [Supported Platforms](../platforms/supported-platforms.md)). Each platform has a **slug** (`snes`, `ps`, `gba`) that doubles as the folder name it looks for in your library. You can override the mapping from folder name to slug via `config.yml`. + +## ROM + +A single game entry. One filesystem file, one folder of files (multi-disc, patched, with DLC), or a manual DB entry. Each ROM belongs to exactly one platform. ROMs get metadata (cover, description, ratings, related games), user data (per-user rating, notes, playtime), and optional assets (saves, states, screenshots, firmware). + +## Metadata Provider + +An external source of game data — IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, PlayMatch, LaunchBox, SteamGridDB, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro. RomM queries one or more of them during a scan and merges the results. Configured via env vars + priority in `config.yml`. See [Metadata Providers](../administration/metadata-providers.md). + +## Scan + +The process of walking the library, hashing files, calling metadata providers, and updating the DB. Scans run in six **modes** (New Platforms, Quick, Unmatched, Update, Hashes, Complete) and can be triggered manually, on a cron, or by the filesystem watcher. See [Scanning & Watcher](../administration/scanning-and-watcher.md). + +## Collection + +A named grouping of ROMs. Three flavours: + +- **Standard** — a hand-curated list. You pick what's in it. +- **Smart** — rule-based and auto-populating. Define a query ("all SNES games rated 4+ stars"); RomM keeps it in sync. +- **Virtual** — auto-generated by RomM itself (by genre, developer, year, tag). Not user-editable; toggle on/off in UI settings. + +## Asset + +User-uploaded content attached to a ROM: save files, emulator states, screenshots. Assets live under `/romm/assets` (separate from the library) and are owned per-user. Saves and states can sync to registered devices. + +## Firmware + +BIOS or system firmware required for certain emulators (PS1, GBA, Saturn, etc.). Lives under `/romm/library/bios/{platform}/` or `/romm/library/{platform}/bios/` (depending on which [folder structure](folder-structure.md) you chose). Uploaded via the UI; managed by admins/editors. + +## User + +An account with a role (Viewer, Editor, Admin) and a set of scopes. Can be created by the Setup Wizard, an admin, an invite link, public registration, or OIDC auto-provisioning. See [Users & Roles](../administration/users-and-roles.md). + +## Role & Scope + +- **Roles** (Viewer / Editor / Admin) are convenient bundles of scopes. +- **Scopes** are fine-grained permissions (`roms.read`, `collections.write`, `users.write`, `tasks.run` — 19 total). + +Tokens and OIDC sessions carry subsets of scopes. Every endpoint requires specific scopes. See the [scope matrix](../administration/users-and-roles.md#scope-matrix). + +## Client API Token + +A long-lived bearer token scoped to a user. Used by companion apps (Argosy, Grout, Playnite, custom scripts) to authenticate. Each user gets up to 25 active tokens; tokens can be paired to devices via a short code. See [Client API Tokens](../ecosystem/client-api-tokens.md). + +## Device + +A registered endpoint that syncs with RomM — a handheld running Grout, an Android phone running Argosy, a SteamDeck running DeckRommSync. Devices pull saves and states; some push them back after a session. See [Device Sync Protocol](../ecosystem/device-sync-protocol.md). + +## Play Session + +A timestamped record of someone playing a ROM — start, end, duration, device. Used by RomM's stats, the Continue Playing ribbon, and per-ROM playtime totals. Ingested automatically when playing in-browser; companion apps push them via API. + +## Task + +A unit of background work — scan, metadata sync, cleanup, device sync. Runs through RQ (Redis Queue). Can be scheduled (cron), watcher-triggered, or manual. See [Scheduled Tasks](../administration/scheduled-tasks.md). + +## Resources & Assets directories (confusing, named similarly, not the same thing) + +- **`/romm/resources`** — **machine-managed**. Cover art, screenshots, manuals fetched from metadata providers. Can be rebuilt from a rescan. +- **`/romm/assets`** — **user-owned**. Saves, states, user-uploaded screenshots. Back this up; it's not recoverable. + +## Console Mode + +A separate `/console` UI optimised for gamepads and TV displays — spatial navigation, bigger hit targets, SFX, no mouse required. Accessible from the same RomM instance, same data. See [Console Mode](../using/console-mode.md). + +## Kiosk Mode + +A server-side setting (`KIOSK_MODE=true`) that turns every read endpoint into unauthenticated access. Anonymous visitors can browse; nobody can write. Useful for public demos and wall displays. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). + +## EmulatorJS / Ruffle + +The two in-browser emulators. **EmulatorJS** handles retro consoles (NES, SNES, N64, PSX, Saturn, and 20+ more cores). **Ruffle** handles Flash / Shockwave browser games. Both launch from the ROM detail page; both can be disabled per-instance if you don't want them. See [In-Browser Play](../using/in-browser-play.md). + +## Netplay + +EmulatorJS's multiplayer mode — two or more players share a session across the internet. RomM tracks open rooms and brokers them via WebSocket. Needs STUN/TURN (ICE servers) configured in `config.yml` for reliable NAT traversal. See [Netplay](../using/netplay.md). + +--- + +That's the whole vocabulary. If something here is still fuzzy, almost every term links to its own page with full detail. diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md index b5c7fff4..065f5d08 100644 --- a/docs/getting-started/first-scan.md +++ b/docs/getting-started/first-scan.md @@ -1,10 +1,86 @@ --- -status: placeholder -wave: 1 +title: Your First Scan +description: Populate a fresh RomM install by scanning the library for the first time. --- # Your First Scan -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +You've got RomM running ([Quick Start](quick-start.md)), your ROMs are laid out correctly ([Folder Structure](folder-structure.md)), and metadata credentials are configured ([Metadata Providers](../administration/metadata-providers.md)). Time to let RomM catalogue everything. + +## Before you hit scan + +A fifteen-second check that saves hours: + +- **Library is mounted** — on the host, `ls /path/to/library` should show your `roms/` (or per-platform) folders. If it doesn't, the mount is wrong. +- **At least one metadata provider is configured** — RomM will scan without one, but every game comes back "unmatched" and you'll have nothing useful to look at. +- **The Setup Wizard is done** — if RomM is still showing the wizard, finish that first. The first user becomes Admin. + +## Run the scan + +1. Click **Scan** in the sidebar. +2. Leave all platforms checked (they're auto-discovered from your folders). +3. Leave all configured providers checked. +4. Leave the mode as the default. +5. Click **Start Scan**. + +The page switches to live mode: + +- A **log** on the right streams everything the scanner is doing — file hashed, provider queried, match found or not. +- Per-platform **accordion panels** show counts update live: total found, matched, unmatched. +- You can click a **matched ROM** while the scan is still running to see what metadata RomM pulled — no need to wait for the full run. + +First scans on big libraries take a while. Expect ~1 second per ROM with a fast network to IGDB/ScreenScraper; hashing (which runs unless you disabled it) adds IO time proportional to file size. + +## What "matched" means + +For each ROM the scanner: + +1. **Computes hashes** (CRC32, MD5, SHA1, plus RetroAchievements-specific hashes). +2. **Hits the configured metadata providers** in priority order. First provider to recognise the file (by hash or filename) becomes the initial match. +3. **Writes the DB entry** with title, cover, description, release date, and anything else the winning provider returned. +4. **Merges overlay data** from other providers — RetroAchievements progression, HowLongToBeat completion times, SteamGridDB alternate covers if you've asked. + +An **unmatched** ROM means no provider recognised it. Common causes: + +- Filename is too generic (`game.gba`). +- Bad rip, intro / patch applied, or a regional variant no provider has indexed. +- Platform folder misnamed — the scanner queries providers scoped to the detected platform; wrong platform = no results. +- Metadata provider credentials wrong or rate-limited — check the scan log for errors. + +Most of these are fixable — see [Scanning Troubleshooting](../troubleshooting/scanning.md). + +## When the scan finishes + +Click the **RomM logo** (top-left) to go home. You should see: + +- Platform cards for each folder it scanned. +- A **Recently Added** carousel on the dashboard. +- A **Continue Playing** section — empty until you play something. + +From here, typical next steps: + +- **Browse** — click a platform card, flip through the grid. +- **Fix unmatched ROMs** — rename or re-tag; re-run an **Unmatched** scan to pick them up. See [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). +- **Tweak priorities** — if ScreenScraper's covers are nicer than IGDB's for your library, reorder `scan.priority.artwork` in [`config.yml`](../reference/configuration-file.md). +- **Add more users** — [Invitations & Registration](../administration/invitations-and-registration.md). + +## Skip to a targeted scan + +If you're adding ROMs later and don't want a full rescan: + +- **New Platforms** — only scans folders RomM hasn't seen before. Fast. +- **Quick** — skips ROMs already catalogued. Good default for "I added a few games". +- **Unmatched** — re-runs matching against ROMs without a provider ID. Good after adding a metadata provider. + +All six scan modes are documented in [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). + +## Automation + +Don't want to keep clicking Scan? + +- **Scheduled scans** run nightly by default. Tune with `SCAN_INTERVAL_CRON`. +- **The filesystem watcher** can auto-scan when files appear or disappear. Enable with `WATCHER_ENABLED=true`. Details and tradeoffs in [Scanning & Watcher](../administration/scanning-and-watcher.md). + +## It's working — what next? + +Back to the [Introduction](../index.md) to pick what you want to do next: set up users, fine-tune admin settings, or just dive in and play. If something's wrong, start with [Scanning Troubleshooting](../troubleshooting/scanning.md). diff --git a/docs/getting-started/folder-structure.md b/docs/getting-started/folder-structure.md index 6f918ee6..ea5a9ea2 100644 --- a/docs/getting-started/folder-structure.md +++ b/docs/getting-started/folder-structure.md @@ -1,10 +1,250 @@ --- -status: placeholder -wave: 1 +title: Folder Structure +description: How to organise your ROM library on disk so RomM can scan and match it. --- + + # Folder Structure -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM expects your library to be organised in one of two layouts. It tries **Structure A** first, and falls back to **Structure B** if A isn't found. This auto-detection is per-library, so you don't pick one up front — just arrange files the way you prefer, and RomM figures it out. + +## The two layouts + +Both layouts separate ROMs from BIOS files. They differ on whether the split lives at the top of the tree or inside each platform. + +- **Structure A (recommended)** — one top-level `roms/`, one top-level `bios/`, platforms nested inside each. + + ```text + /roms/{platform}/ + /bios/{platform}/ + ``` + +- **Structure B (fallback)** — one folder per platform at the top, `roms/` and `bios/` inside each. + + ```text + /{platform}/roms/ + /{platform}/bios/ + ``` + +The BIOS / firmware tree is **optional** — only platforms that require firmware for emulation need it. + +### Mount point + +The path you mount into the RomM container as `/romm/library` depends on which structure you pick: + +- **Structure A**: mount the parent of the `roms/` folder. +- **Structure B**: mount the parent of the platform folders. + +See the [reference Docker Compose](../install/docker-compose.md) for where `/romm/library` lives. + +## Multi-file games + +Some games come as **folders** instead of single files — multi-disc, DLC, manuals, patches. RomM understands this layout and recognises these sub-folder names, surfacing them as tags in the UI: + +- `dlc` +- `hack` +- `manual` +- `mod` +- `patch` +- `update` +- `demo` +- `translation` +- `prototype` + +!!! tip "Platform folder names" + The platform folder name has to match a known slug (`gbc`, `gba`, `ps`, `snes`, etc.). Full list in [Supported Platforms](../platforms/supported-platforms.md). If your existing folder names don't match (say, `super_nintendo/` instead of `snes/`), override the mapping via `system.platforms` in [`config.yml`](../reference/configuration-file.md). + +## Visual reference + + + + + + + + + + + + + + +
Structure A (recommended)Structure B (fallback)
+ library/roms/{platform}/{game} + + library/{platform}/roms/{game} +
+
+        library/
+        ├─ roms/
+        │  ├─ gbc/
+        │  │  ├─ game_1.gbc
+        │  │  └─ game_2.gbc
+        │  │
+        │  ├─ gba/
+        │  │  ├─ game_3.gba
+        │  │  └─ game_4/
+        │  │     ├─ game_4.gba
+        │  │     ├─ dlc
+        │  │     │  ├─ game_4_dlc_1.7z
+        │  │     │  └─ game_4_dlc_2.7z
+        │  │     ├─ hack
+        │  │     │  └─ game_4_hardmode.rar
+        │  │     ├─ manual
+        │  │     │  └─ game_4_manual.pdf
+        │  │     ├─ mod
+        │  │     │  └─ game_4_crazy_mode.zip
+        │  │     ├─ patch
+        │  │     │  └─ game_4_patch_v1.1.zip
+        │  │     ├─ update
+        │  │     ├─ demo
+        │  │     ├─ translation
+        │  │     └─ prototype
+        │  │
+        │  └─ ps/
+        │     ├─ game_5/
+        │     │   ├─ game_5_cd_1.iso
+        │     │   └─ game_5_cd_2.iso
+        │     │
+        │     └─ game_6.iso
+        │
+        └─ bios/
+           ├─ gba/
+           │  └─ gba_bios.bin
+           │
+           └─ ps/
+              ├─ scph1001.bin
+              ├─ scph5501.bin
+              └─ scph5502.bin
+        
+
+
+        library/
+        ├─ gbc/
+        │  └─ roms/
+        │     ├─ game_1.gbc
+        │     └─ game_2.gbc
+        │
+        ├─ gba/
+        │  ├─ roms/
+        │  │  ├─ game_3.gba
+        │  │  └─ game_4/
+        │  │     ├─ game_4.gba
+        │  │     ├─ dlc
+        │  │     ├─ hack
+        │  │     ├─ manual
+        │  │     ├─ mod
+        │  │     ├─ patch
+        │  │     ├─ update
+        │  │     ├─ demo
+        │  │     ├─ translation
+        │  │     └─ prototype
+        │  │
+        │  └─ bios/
+        │     └─ gba_bios.bin
+        │
+        └─ ps/
+           ├─ roms/
+           │  ├─ game_5/
+           │  │  ├─ game_5_cd_1.iso
+           │  │  └─ game_5_cd_2.iso
+           │  │
+           │  └─ game_6.iso
+           │
+           └─ bios/
+              ├─ scph1001.bin
+              ├─ scph5501.bin
+              └─ scph5502.bin
+        
+
+ +!!! note "Starting from scratch?" + RomM can also bootstrap an empty library. If you upload files through the web UI without any existing structure, RomM creates **Structure A** on your behalf. + +## Customising behaviour + +The on-disk layout is only half the story. Per-library exclusions, custom platform bindings, and metadata source priority all live in [`config.yml`](../reference/configuration-file.md). You can edit the file directly, or go through **Administration → Library Management** in the web UI — they're two views of the same data. + +## Naming convention + +Filenames are parsed for region, language, revision, and arbitrary tags. Both `[]` and `()` delimiters work. + +- **Region / language** — both ISO-like codes and full names. Add a custom region or language by prefixing with `reg` / `reg-` (e.g. `reg MyOwnLang` or `reg-MyOwnLang`). +- **Revision** — prefix with `rev` / `rev-`. Example: `rev v1`, `rev-1`. +- **Arbitrary tags** — anything else in brackets is imported verbatim. Example: `tetris [1.0001](HACK)[!].gba`. + +Tags are searchable in the search bar — typing `(USA)` returns every game tagged USA. + +
+ +
+ +### Supported languages + +| Code | Language | +| ------ | ----------- | +| Ar | Arabic | +| Da | Danish | +| De | German | +| El | Greek | +| En | English | +| Es | Spanish | +| Fi | Finnish | +| Fr | French | +| It | Italian | +| Ja | Japanese | +| Ko | Korean | +| Nl | Dutch | +| No | Norwegian | +| Pl | Polish | +| Pt | Portuguese | +| Ru | Russian | +| Sr | Serbian | +| Sv | Swedish | +| Zh | Chinese | +| nolang | No Language | + +
+ +
+ +### Supported regions + +| Code | Region | +| ---- | ------------- | +| A | Australia | +| AS | Asia | +| B | Brazil | +| C | Canada | +| CH | China | +| E | Europe | +| F | France | +| FN | Finland | +| G | Germany | +| GR | Greece | +| H | Holland | +| HK | Hong Kong | +| I | Italy | +| J | Japan | +| K | Korea | +| NL | Netherlands | +| NO | Norway | +| PD | Public Domain | +| R | Russia | +| S | Spain | +| SW | Sweden | +| T | Taiwan | +| U | USA | +| UK | England | +| UNK | Unknown | +| UNL | Unlicensed | +| W | World | + +
+ +
+ +## Filename metadata tags + +RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry — covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). diff --git a/docs/getting-started/what-is-new-in-5.md b/docs/getting-started/what-is-new-in-5.md index 9958abd0..2f737c85 100644 --- a/docs/getting-started/what-is-new-in-5.md +++ b/docs/getting-started/what-is-new-in-5.md @@ -1,10 +1,137 @@ --- -status: placeholder -wave: 1 +title: What's New in 5.0 +description: The highlights of the RomM 5.0 release — features, changes, and what it means for you. --- # What's New in 5.0 -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM 5.0 is the biggest release since the project started. Here's what's landed, organised by who it's for. + +!!! warning "Upgrading from 4.x?" + Read the [**Upgrading to 5.0**](../releases/upgrading-to-5.0.md) guide before you pull the new image. There are breaking changes — migrations, env var renames, and config shifts. + +## For end users + +### Console Mode + +A brand-new `/console` UI designed for TVs and gamepads. Spatial navigation, focus sounds, large hit targets, gamepad-first controls. Built as a separate SPA sharing the same data as the main UI — no extra deploy, just open `/console`. [Read more →](../using/console-mode.md) + +### Smart & Virtual Collections + +**Standard** collections (curated by you) now join two new flavours: + +- **Smart Collections** — rule-based, auto-populating. Define a query, RomM keeps it in sync. +- **Virtual Collections** — auto-generated by RomM by genre, developer, year, or tag. + +[Collections → ](../using/collections.md) + +### ROM Patcher + +Apply IPS / UPS / BPS / PPF / RUP / APS / BDF / PMSR / vcdiff patches in the browser. Save the result locally or push it back into the library. [ROM Patcher →](../using/rom-patcher.md) + +### Netplay + +Multiplayer for EmulatorJS sessions — host or join rooms, with ICE server configuration for NAT traversal. [Netplay →](../using/netplay.md) + +### PWA install + +RomM ships with a proper manifest and service worker. Install to your home screen on iOS / Android / desktop for an app-like experience. [Install as PWA →](../using/pwa.md) + +### 19 locales + +English (US/GB), Spanish, French, German, Italian, Portuguese (BR), Japanese, Korean, Russian, Polish, Czech, Hungarian, Romanian, Bulgarian, Chinese (Simplified/Traditional). Switch in User Interface settings. [Languages →](../using/languages.md) + +### Time to Beat & RetroAchievements tabs + +HowLongToBeat completion times and RetroAchievements progression now surface as dedicated tabs on every game's detail page. Both need provider credentials — see [Metadata Providers](../administration/metadata-providers.md). + +### Bulk downloads, QR codes, copy-link + +Every ROM gets a one-click Copy Link button and a QR code for mobile sharing. Bulk downloads stream multi-ROM zips via nginx's `mod_zip`. + +## For operators + +### Upgrade-grade migration + +4.x → 5.0 is a schema-migrating upgrade. Alembic runs on startup and takes care of the heavy lifting, but you should [back up first](../install/backup-and-restore.md). Full migration playbook in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). + +### Thirteen metadata providers + +Up from nine. New in 5.0: **TheGamesDB**, **Libretro metadata**, **gamelist.xml importer** (for ES-DE / Batocera migrations). [Metadata Providers →](../administration/metadata-providers.md) + +### Slim + Full images + +Two official image variants: + +- **Full** (default) — includes EmulatorJS and Ruffle. ~1.2 GB. +- **Slim** — no in-browser emulators. ~400 MB. + +Pick based on whether you want in-browser play. [Image Variants →](../install/image-variants.md) + +### Expanded OIDC + +- **Role mapping** from OIDC claims to RomM roles (`OIDC_CLAIM_ROLES`, `OIDC_ROLE_VIEWER/EDITOR/ADMIN`). +- **RP-initiated logout** support. +- **Autologin** (skip the RomM login page and bounce straight to the IdP). +- Keycloak is now a first-class guide alongside Authelia, Authentik, PocketID, Zitadel. + +[OIDC Setup →](../administration/oidc/index.md) + +### Invite links + public registration + +First-class invite flow (`INVITE_TOKEN_DAYS`). Opt-in public self-registration (`ALLOW_PUBLIC_REGISTRATION`) for instances already behind upstream auth. [Invitations & Registration →](../administration/invitations-and-registration.md) + +### Real-time filesystem watcher + +Toggle-able `WATCHER_ENABLED` with debounce, extension filtering, and intelligent batching. Pairs well with the scheduled scan. [Scanning & Watcher →](../administration/scanning-and-watcher.md) + +### Observability + +- **OpenTelemetry** export via `OTEL_ENABLED`. +- **Sentry** integration (opt-in). +- Per-task status in the admin UI and via `/api/heartbeat`. + +[Observability →](../administration/observability.md) + +### Kubernetes-ready + +First-class K8s manifest examples, including the mandatory `enableServiceLinks: false` gotcha. No official Helm chart yet, but the community charts on ArtifactHub are reasonable. [Kubernetes →](../install/kubernetes.md) + +## For ecosystem developers + +### Client API Tokens + device pairing + +Per-user bearer tokens (max 25) with a subset of scopes, optionally paired to a device via a short code — so your handheld app doesn't have to type a 40-character secret. [Client API Tokens →](../ecosystem/client-api-tokens.md) + +### Device Sync protocol + +New endpoints under `/api/devices/`, `/api/sync/`, and `/api/play-sessions/` for bidirectional save/state sync and play-session ingestion. Grout, Argosy, and DeckRommSync are early consumers. [Device Sync Protocol →](../ecosystem/device-sync-protocol.md) + +### Expanded feed endpoints + +Existing WebRcade and Tinfoil feeds now joined by PKGi (PS3/Vita/PSP), FPKGi (PS4/PS5), Kekatsu (DS), and PKGj formats. [Feeds reference →](../reference/feeds.md) + +### Gamelist.xml + Pegasus export + +Export your RomM library to ES-DE / Batocera gamelist.xml or Pegasus metadata format, for users bouncing between frontends. + +### WebSockets + +Two endpoints: `/ws/socket.io` for general live updates (scan progress, notifications) and `/netplay/socket.io` for netplay session coordination. Redis-backed pubsub for multi-instance deployments. [WebSockets →](../developers/websockets.md) + +## Breaking changes at a glance + +| Area | What changed | +| --- | --- | +| **Database schema** | Alembic migrates automatically — but back up first. | +| **Env var names** | A few renames around scheduled-task cron vars and OIDC role mapping. See [migration table](../releases/upgrading-to-5.0.md). | +| **`config.yml`** | New sections for `scan.region`, `scan.language`, `scan.media`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings carry over. | +| **URL structure** | Docs URLs restructured (you're reading the new ones). Every old URL redirects — see [the redirect map](../releases/upgrading-to-5.0.md) in the migration guide. | +| **Image tags** | `:slim` and `:5.0.0-slim` are new options alongside `:latest` and `:5.0.0`. | + +## Where to go from here + +- **Upgrading**: [Upgrading to 5.0](../releases/upgrading-to-5.0.md) +- **Fresh install**: [Quick Start](quick-start.md) +- **What does X mean?**: [Core Concepts](concepts.md) +- **Full changelog**: [Changelog](../releases/changelog.md) From 43f04ba4144633ccead61d9c59fa680ccce6cd48 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 17:35:19 +0000 Subject: [PATCH 008/121] docs: draft Wave 1 developers + troubleshooting pages (8 pages) Developers: - developers/development-setup.md: ported with the full Docker and manual setup paths, RAHasher optional build, Trunk + pytest workflow, dev-URL cheatsheet, architecture pointer - developers/contributing.md: ported with the AI-assistance disclosure rule kept prominent, PR guidelines refreshed, docs/ translation/bug-report paths clarified Troubleshooting (all 6 pages): - troubleshooting/index.md: NEW symptom-keyed router with "by symptom" categories and out-links to topic pages - troubleshooting/scanning.md: expanded from the old page with metadata-provider errors, unmatched-ROM strategies, hash tuning, "scan seems to work but nothing shows up" diagnostic - troubleshooting/authentication.md: expanded with a full OIDC section covering redirect_uri_mismatch, role-mapping failures, Authentik 2025.10 email_verified, Keycloak unverified email, Zitadel "Email is missing from token", autologin loops, the DISABLE_USERPASS_LOGIN lock-out recovery - troubleshooting/synology.md: kept the ErrNo 13 permission runbook, added DSM MariaDB port collision and volume drift - troubleshooting/kubernetes.md: expanded from the 24-line stub with 413 request-size fix, WebSocket annotations, permission init container, OOMKilled tuning, startup ordering - troubleshooting/miscellaneous.md: SQLite persistence footgun, IGDB creds, log-grepping recipes per deployment, blank-UI diagnostics, upgrade rollback Builds clean with --strict. --- docs/developers/contributing.md | 125 ++++++++++++++- docs/developers/development-setup.md | 207 ++++++++++++++++++++++++- docs/troubleshooting/authentication.md | 126 ++++++++++++++- docs/troubleshooting/index.md | 49 +++++- docs/troubleshooting/kubernetes.md | 128 ++++++++++++++- docs/troubleshooting/miscellaneous.md | 99 +++++++++++- docs/troubleshooting/scanning.md | 130 +++++++++++++++- docs/troubleshooting/synology.md | 69 ++++++++- 8 files changed, 888 insertions(+), 45 deletions(-) diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index 64041efd..76633f51 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -1,10 +1,125 @@ --- -status: placeholder -wave: 1 +title: Contributing +description: How to contribute code, docs, translations, and bug reports to RomM. --- # Contributing -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Thanks for considering contributing. A few ground rules up front, then the mechanics. + +!!! important "Big changes — open an issue first" + If you're planning a large feature or architectural change, open a GitHub issue **and** hop into the [Discord](https://discord.gg/romm) to discuss with maintainers before coding. A rejected 2,000-line PR is nobody's idea of fun. + +## Code of Conduct + +The project follows the [Contributor Covenant](https://github.com/rommapp/romm/blob/master/CODE_OF_CONDUCT.md). By contributing, you agree to uphold it. + +## AI assistance — please disclose + +!!! warning "Required disclosure" + If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR — including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count; anything more does. If PR responses are generated by an AI, disclose that too. + +Example disclosures: + +> This PR was written primarily by Claude Code. + +Or more granularly: + +> I consulted ChatGPT to understand the codebase, but the solution was fully authored manually. + +This isn't about gatekeeping AI contributions — it's about letting reviewers know how much scrutiny to apply. AI-assisted code often ranges from "fantastic" to "hallucinated nonsense"; we need to calibrate. + +Failing to disclose is rude to the humans reviewing your PR. Don't do it. + +## What you can contribute + +### Code + +- **Features** — open an issue first for anything non-trivial. +- **Bug fixes** — happy to take these without pre-discussion if they're small and focused. +- **Refactors** — open an issue first. Refactors without a clear user-facing win are usually rejected. + +### Documentation + +You're on the docs site right now. If something's wrong, unclear, or missing: + +- PRs welcome against [rommapp/docs](https://github.com/rommapp/docs). +- Small fixes (typos, broken links) don't need an issue first. +- Bigger changes (restructuring, adding a new section) — open an issue first or ping in Discord. + +Building docs locally is covered in the docs repo's `CONTRIBUTING.md`. Short version: + +```sh +uv venv && source .venv/bin/activate +uv sync --all-extras --dev +uv run mkdocs serve +``` + +### Translations + +Create a new folder under `frontend/src/locales/` using the existing language files as a template, translate the strings, and open a PR. Partial translations are welcome — we'd rather have an 80%-translated locale than nothing. + +See [Translations (i18n)](i18n.md) for the full translator workflow. + +### Bug reports + +[Open an issue](https://github.com/rommapp/romm/issues) with: + +- What happened, what you expected. +- Exact reproduction steps. +- RomM version and how you deployed it (Docker tag, Unraid, K8s, etc.). +- Relevant logs (`docker logs romm` — redact any secrets). + +The bug report template prompts for all of this. + +## Pull request mechanics + +1. Fork the repository on GitHub. +2. Clone your fork: + ```sh + git clone https://github.com//romm.git + ``` +3. Check out `master`: + ```sh + cd romm && git checkout master + ``` +4. Set up your dev environment — [Development Setup](development-setup.md). +5. Create a branch: + ```sh + git checkout -b short-feature-name + ``` +6. Make the change. Commit with descriptive messages: + ```sh + git commit -am "Add support for IGDB alternate-name matching" + ``` +7. Push: + ```sh + git push origin short-feature-name + ``` +8. Open a PR against `master` on `rommapp/romm`. + +## PR guidelines + +- **Lint clean.** `trunk check` must pass — CI will block otherwise. See [Development Setup → Linting](development-setup.md#linting). +- **Tests pass + new tests for new code.** `uv run pytest` must pass. New behaviour needs coverage. +- **Docs updated** when behaviour changes. Even a one-line update to the relevant docs page is better than stale docs. +- **Clear title + description.** "Fix bug" isn't a title. "Fix scan skipping multi-disc PS1 games when first disc is a .chd" is. +- **Keep the scope tight.** One concern per PR. Drive-by refactors are fine if small, scorched-earth rewrites aren't. + +## Code style + +Match the existing style. If you use VS Code (or a compatible editor), these extensions match what maintainers use: + +- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +- [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) +- [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) +- [Ruff](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) +- [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) + +## Licensing + +By contributing, you agree that your contributions are licensed under the project's [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE) — AGPL-3.0 for the core app. + +--- + +Thanks for helping make RomM better. diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index 77b1c466..08967656 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -1,10 +1,207 @@ --- -status: placeholder -wave: 1 +title: Development Setup +description: Run RomM locally for development — Docker, manual, tests, lint. --- # Development Setup -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Two paths: **Docker** (recommended — matches production closely) or **manual** (edit Python on your host, faster iteration cycles). + +If you're contributing, also read [Contributing](contributing.md). + +## Option 1 — Docker + +Simplest. One command brings up the whole stack. + +### 1. Clone + mock the library + +Get the code and create the minimal fixtures RomM expects at runtime: + +```sh +git clone https://github.com/rommapp/romm.git +cd romm + +mkdir -p romm_mock/library/roms/switch +touch romm_mock/library/roms/switch/metroid.xci +mkdir -p romm_mock/resources romm_mock/assets romm_mock/config +touch romm_mock/config/config.yml +``` + +### 2. `.env` + +```sh +cp env.template .env +``` + +Then edit `.env` to set dev defaults: + +```dotenv +ROMM_BASE_PATH=/app/romm +DEV_MODE=true +``` + +### 3. Build + bring up + +```sh +docker compose build # --no-cache to force a clean rebuild +docker compose up -d +``` + +That's it. RomM is on `http://localhost:3000`. Source mounts are live — edit backend or frontend code and changes reflect on the next request (backend) or instantly (frontend HMR). + +## Option 2 — Manual + +Faster iteration cycles than Docker if you're touching Python a lot. + +### 1. Clone + mock the library + +Same as Docker Option 1. + +```sh +git clone https://github.com/rommapp/romm.git +cd romm + +mkdir -p romm_mock/library/roms/switch +touch romm_mock/library/roms/switch/metroid.xci +mkdir -p romm_mock/resources romm_mock/assets romm_mock/config +touch romm_mock/config/config.yml + +cp env.template .env +``` + +### 2. System dependencies + +```sh +# Debian / Ubuntu +sudo apt install libmariadb3 libmariadb-dev libpq-dev +``` + +Adjust for your distro; the key libraries are the MariaDB connector and libpq for Postgres. + +#### RAHasher (optional) + +Only needed if you're working on RetroAchievements hash calculation. **Not supported on macOS — skip.** + +```sh +git clone --recursive https://github.com/RetroAchievements/RALibretro.git +cd RALibretro +git checkout 1.8.0 +git submodule update --init --recursive +sed -i '22a #include ' ./src/Util.h +make HAVE_CHD=1 -f ./Makefile.RAHasher +sudo cp ./bin64/RAHasher /usr/bin/RAHasher +cd .. +``` + +### 3. Python environment + +RomM uses [uv](https://docs.astral.sh/uv/getting-started/installation/): + +```sh +curl -LsSf https://astral.sh/uv/install.sh | sh + +uv venv +source .venv/bin/activate +uv sync --all-extras --dev +``` + +### 4. Start supporting services + +MariaDB and Redis come up via the dev compose file: + +```sh +docker compose up -d +``` + +### 5. Run the backend + +Alembic migrations run automatically on start. + +```sh +cd backend +uv run python3 main.py +``` + +### 6. Frontend + +In a second terminal: + +```sh +cd frontend +# needs Node.js 24+ and npm >= 9 +npm install +``` + +Symlink the mock `resources/` and `assets/` into the frontend's serving tree so uploaded-media URLs resolve during dev: + +```sh +mkdir -p assets/romm +ln -s ../../romm_mock/resources assets/romm/resources +ln -s ../../romm_mock/assets assets/romm/assets + +npm run dev +``` + +Frontend is on `http://localhost:3000`; it proxies API calls through to the backend. + +## Linting + +RomM uses [Trunk](https://trunk.io) as a meta-linter — it wraps ruff, prettier, eslint, markdownlint, and a few others under one config. + +```sh +curl https://get.trunk.io -fsSL | bash + +trunk fmt # auto-fix what it can +trunk check # report what it can't +``` + +Trunk runs as a pre-commit hook automatically after install. Alternative install methods are in [the Trunk docs](https://docs.trunk.io/check/usage#install-the-cli). + +!!! warning "CI blocks un-linted PRs" + Trunk's check runs on every PR. If it fails, your PR can't merge — same rules as the maintainers. + +## Tests + +### One-time: create the test DB + user + +```sh +docker exec -i romm-db-dev mariadb -uroot -p < backend/romm_test/setup.sql +``` + +The password is whatever you set for `MARIADB_ROOT_PASSWORD` in `.env`. + +### Run + +Migrations run automatically before tests. + +```sh +cd backend +uv run pytest # all tests +uv run pytest path/to/file.py # one file +uv run pytest -vv # verbose +uv run pytest -k scan # match by name +``` + +## Useful dev URLs + +| URL | Purpose | +| --- | --- | +| `http://localhost:3000` | Main UI | +| `http://localhost:3000/api/docs` | Swagger UI — try endpoints live | +| `http://localhost:3000/api/redoc` | ReDoc-rendered API reference | +| `http://localhost:3000/openapi.json` | OpenAPI spec — source of truth for client generators | +| `http://localhost:3000/api/heartbeat` | Health + config snapshot | + +## Architecture at a glance + +If you're new to the codebase, read [Architecture](architecture.md) for a high-level walkthrough, then come back here. The short version: + +- `backend/` — FastAPI, SQLAlchemy, Alembic, RQ workers. +- `frontend/` — Vue 3 + Vuetify + Pinia + Vite. Separate `/console` SPA for TV/gamepad mode. +- `docker/` — nginx config (with `mod_zip`), entrypoint scripts, multi-stage Dockerfiles. +- `examples/` — reference compose files. + +## Getting stuck + +- The `#dev` channel on the [RomM Discord](https://discord.gg/romm) is the fastest unblock path. +- File an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) if it's reproducible and you've got steps. diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index c133a664..b0ef5b0e 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -1,10 +1,124 @@ --- -status: placeholder -wave: 1 +title: Authentication Troubleshooting +description: Fix login, session, CSRF, and OIDC issues. --- -# Authentication +# Authentication Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +## `403 Forbidden` on API calls + +When auth is enabled (almost always), any endpoint that requires a session returns `403` if: + +- You're not authenticated. +- Your session is in a broken state (expired, signed with an old secret, missing CSRF). + +Fix: [clear cookies](https://support.google.com/accounts/answer/32050) for the RomM host and sign in again. + +## `Forbidden (403) CSRF verification failed` + +CSRF protection is on by default. A mismatched or missing `csrftoken` cookie causes this. + +1. Reload the page — RomM sets a fresh CSRF cookie on GET requests, which should fix it on the next POST. +2. Still broken? Clear cookies for the RomM host and hard-reload (`CMD+SHIFT+R` / `CTRL+F5`). +3. Known to happen on Chrome specifically; rare on Firefox/Safari. + +If you're behind a reverse proxy and CSRF keeps failing, the proxy is probably stripping the `csrftoken` cookie or the `X-CSRFToken` header. See the [Reverse Proxy recipes](../install/reverse-proxy.md) — every one of them forwards `Cookie` and all custom headers by default, so if yours doesn't, fix the proxy config. + +## `400 Bad Request` on the WebSocket endpoint + +Your reverse proxy is stripping the WebSocket upgrade. RomM uses Socket.IO for live updates (scan progress, Netplay). + +Fixes per proxy: + +- **Nginx / NPM** — enable WebSockets Support; the [Reverse Proxy](../install/reverse-proxy.md) snippets already do this. +- **Traefik** — add `proxy_set_header Upgrade $http_upgrade` (or use the Traefik middleware equivalent). +- **Caddy** — WebSockets work out of the box with `reverse_proxy`. +- **Cloudflare** — enable **WebSockets** under Network settings. + +## `Error: Could not get twitch auth token: check client_id and client_secret` + +IGDB creds are wrong or revoked on the Twitch side. + +1. Go to [dev.twitch.tv/console/apps](https://dev.twitch.tv/console/apps). +2. Verify your RomM application still exists. +3. Regenerate the Client Secret; copy both Client ID and Client Secret. +4. Update `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` in your RomM env. +5. `docker compose up -d` to pick up the new values. + +## Password logins are disabled, OIDC is broken, I'm locked out + +You set `DISABLE_USERPASS_LOGIN=true` and now OIDC isn't working. + +1. Edit your compose / env to unset `DISABLE_USERPASS_LOGIN` (or set it to `false`). +2. `docker compose up -d` to restart with the new config. +3. Log in with your local admin. +4. Fix OIDC — see below. +5. Re-enable `DISABLE_USERPASS_LOGIN` only after confirming OIDC works end-to-end. + +This is the reason [OIDC Setup](../administration/oidc/index.md) tells you to verify OIDC before turning off local login. + +## OIDC + +### `redirect_uri_mismatch` + +The `OIDC_REDIRECT_URI` in RomM's env doesn't **exactly** match what's registered at the IdP. + +Check for: + +- **Trailing slashes.** `/api/oauth/openid` vs `/api/oauth/openid/` are different to the IdP. +- **Scheme.** `http://` vs `https://`. +- **Host.** `romm.example.com` vs `www.romm.example.com` vs the bare IP. +- **Port.** Implied `80` / `443` on HTTPS vs an explicit port. + +Fix: make them identical on both sides. + +### User is created but stays Viewer, even though they should be Admin + +You configured `OIDC_CLAIM_ROLES` but RomM isn't honouring it. + +1. **Is the claim actually in the token?** Decode your IdP's ID token at [jwt.io](https://jwt.io) and verify the claim name (e.g. `groups`, `realm_access.roles`) is present and non-empty. +2. **Does the value match?** `OIDC_ROLE_ADMIN=romm-admin` will only match if the claim contains exactly the string `romm-admin`. Case-sensitive. +3. **Is the claim mapper on the IdP side configured to include the claim?** On Keycloak, for example, you need a Client Scope with a Group Membership mapper added to the client. + +Roles are re-evaluated on every login — there's no cache to bust. Log out and back in after fixing. + +### "Email is missing from token" (Zitadel-specific) + +On Zitadel, open the application → **Token Settings** → tick **User Info inside ID Token** → Save. + +See [OIDC with Zitadel → Enable claims](../administration/oidc/zitadel.md) for the full walkthrough. + +### Authentik 2025.10: login succeeds but RomM rejects the user + +Authentik 2025.10 changed the default `email_verified` claim from `true` to `false`. RomM requires a verified email, so the claim must arrive as `true`. + +Fix: add the property mapping documented in [OIDC with Authentik → Create a property mapping](../administration/oidc/authentik.md#2-create-a-property-mapping-authentik-202510). + +### Keycloak: user created in RomM but can't log in + +Two possibilities: + +1. **Email not verified in Keycloak.** Admin Console → Users → open the user → **Email Verified**: on. RomM rejects unverified emails. +2. **Email mismatch between Keycloak and a pre-existing RomM user.** If RomM already has a local account `alice@example.com`, the first OIDC login for `alice@example.com` signs into that account. If the emails don't match exactly, RomM creates a *second* account. Fix: edit the user in RomM to set the correct email, then log in via OIDC. + +### `OAuthException: expired token` on callback + +Your RomM host and the IdP have significant clock drift. Run NTP on both. + +### Autologin loops forever + +You set `OIDC_AUTOLOGIN=true` and your IdP keeps bouncing you back to RomM, which bounces you back to the IdP. + +Usually because something else in the chain (a CSRF check, a cookie domain mismatch, a reverse-proxy rewrite) is breaking the post-callback handoff. To escape: + +1. Hit `/login?bypass_autologin=true` directly to land on the normal login page. +2. Sign in as a local admin. +3. Disable `OIDC_AUTOLOGIN`, restart, and debug the IdP config with autologin off. + +If `bypass_autologin` doesn't work in your version, shell into the container and unset `OIDC_AUTOLOGIN` in the env, or edit your compose and restart. + +## Still stuck + +- Check the container logs: `docker logs romm 2>&1 | grep -iE 'auth|oidc|oauth'`. +- Cross-reference your IdP's audit logs — they often show exactly why a login was rejected on their side. +- Ask on [Discord](https://discord.gg/romm) `#help` with the IdP name and the exact error text. diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md index 91a87636..e419f08b 100644 --- a/docs/troubleshooting/index.md +++ b/docs/troubleshooting/index.md @@ -1,10 +1,49 @@ --- -status: placeholder -wave: 1 +title: Troubleshooting +description: Diagnose common RomM issues by symptom. --- # Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Something's broken. Start with the symptom that best matches: + +## By symptom + +### RomM won't start + +- Container crashes immediately → check `docker logs romm`. If it's `invalid host in "tcp://..."`, you're on Kubernetes — see [Kubernetes Troubleshooting](kubernetes.md). +- Database connection errors → verify `DB_HOST` / `DB_PASSWD` match your DB container, and that the DB has finished initialising (first run takes longer than you'd think). +- "Page not found" on first load → wait; initial migrations and resource seeding take a minute. + +### Login issues + +- Getting 403s → [Authentication Troubleshooting](authentication.md). +- OIDC redirect fails → [Authentication Troubleshooting → OIDC](authentication.md#oidc). +- Forgot the admin password → reset via DB or a fresh admin invite from another admin account. + +### Scanning problems + +- Scan ends immediately, no platforms found → [Scanning Troubleshooting](scanning.md). +- Platform not detected → check folder slug matches [Supported Platforms](../platforms/supported-platforms.md) or add a `system.platforms` binding in [`config.yml`](../reference/configuration-file.md). +- Scan times out → [Scanning Troubleshooting](scanning.md). + +### In-browser play + +- EmulatorJS won't load / cores 404 → [In-Browser Play Troubleshooting](in-browser-play.md). Probably the slim image without `ENABLE_EMULATORJS` set. +- Netplay doesn't connect → [Netplay Troubleshooting](netplay.md). Almost always NAT / missing ICE servers. + +### Device sync + +- Grout / Argosy / DeckRommSync not syncing → [Device Sync Troubleshooting](sync.md). + +### Platform-specific + +- [Synology Troubleshooting](synology.md) — permission errors, DSM gotchas. +- [Kubernetes Troubleshooting](kubernetes.md) — the `enableServiceLinks` fix and related. +- [Miscellaneous Troubleshooting](miscellaneous.md) — everything else. + +## Still stuck + +- [GitHub issues](https://github.com/rommapp/romm/issues) — search first; open if you've got a reproducible bug. +- [Discord](https://discord.gg/romm) — `#help` channel, staffed by community + maintainers. +- Logs to include when asking for help: `docker logs romm` (redact secrets), your RomM version (top of the About modal in the profile drawer), and the exact steps you took. diff --git a/docs/troubleshooting/kubernetes.md b/docs/troubleshooting/kubernetes.md index 6d9abd12..58501b60 100644 --- a/docs/troubleshooting/kubernetes.md +++ b/docs/troubleshooting/kubernetes.md @@ -1,10 +1,126 @@ --- -status: placeholder -wave: 1 +title: Kubernetes Troubleshooting +description: Fix Kubernetes-specific RomM issues. --- -# Kubernetes +# Kubernetes Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +## `invalid host in "tcp://:8080" of the "listen" directive` + +The flagship K8s gotcha. Kubernetes auto-injects service addresses as env vars (`SERVICENAME_PORT=tcp://...`), which RomM's nginx then tries to bind to. + +Fix: disable service-link env vars on the RomM pod. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: romm + namespace: romm +spec: + template: + spec: + enableServiceLinks: false # ← this line +``` + +Covered in full in the [Kubernetes install guide](../install/kubernetes.md#required-quirk-enableservicelinks-false). + +## Large uploads rejected with `413 Request Entity Too Large` + +The ingress controller is capping request body size. Default for nginx-ingress is 1 MB — won't survive a single ROM upload. + +Add the annotation: + +```yaml +metadata: + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: "0" +``` + +Traefik equivalent: + +```yaml +metadata: + annotations: + traefik.ingress.kubernetes.io/request-maxsize: "0" +``` + +Cloudflare (when in front of your ingress): check plan limits. Free tier caps uploads at 100 MB regardless of what your cluster allows. + +## WebSockets disconnect immediately + +Ingress isn't forwarding the WebSocket upgrade. + +nginx-ingress: + +```yaml +metadata: + annotations: + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" +``` + +## Pod crashes on startup: `permission denied` writing to `/romm/resources` + +The container is running as a non-root user but the PVC came up with wrong ownership. + +Two fixes: + +- **Init container** that chowns the PV on first start: + + ```yaml + initContainers: + - name: fix-permissions + image: busybox + command: ["sh", "-c", "chown -R 1000:1000 /romm/resources /romm/assets /romm/config /redis-data"] + volumeMounts: + - { name: resources, mountPath: /romm/resources } + - { name: assets, mountPath: /romm/assets } + - { name: config, mountPath: /romm/config } + - { name: redis-data, mountPath: /redis-data } + securityContext: + runAsUser: 0 + ``` + +- **Storage class that supports `fsGroup`** — add `fsGroup: 1000` to the pod's `securityContext`. Works on most CSI drivers but not all. + +## Pod can reach the DB but RomM crashes with `ConnectionRefused` + +Startup ordering. RomM starts before the DB is ready, fails, and crashlooped-restarts forever because the restart is too fast for the DB to catch up. + +Fix: add an init container that waits, or a `readinessProbe` + generous `startupProbe` on the DB StatefulSet. The [Kubernetes install guide](../install/kubernetes.md#mariadb) has a readiness probe baked in — check you're using it. + +## Scheduler tasks don't run + +RomM schedules tasks via RQ. If Redis is an external service and the RomM pod can't reach it, scheduled tasks silently don't fire. + +Check: + +```sh +kubectl exec -n romm deploy/romm -- redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWD ping +# should print "PONG" +``` + +If that fails, network policy is blocking the pod or the Redis service name doesn't resolve. + +## `OOMKilled` during large scans + +Scans of big libraries with hash calculation spike memory. Default pod memory limits are often too tight. + +Raise the limit: + +```yaml +resources: + requests: + memory: "1Gi" + limits: + memory: "4Gi" +``` + +Or disable hashing on the Scan page to cut memory use by ~80% (you lose RetroAchievements + Hasheous matching — see [Metadata Providers](../administration/metadata-providers.md)). + +## Still stuck + +- Full install reference: [Kubernetes](../install/kubernetes.md). +- [Discord](https://discord.gg/romm) `#kubernetes` channel. +- Include your ingress controller, storage class, and the exact error from `kubectl logs` when asking for help. diff --git a/docs/troubleshooting/miscellaneous.md b/docs/troubleshooting/miscellaneous.md index 3a4e49bb..f48b33f6 100644 --- a/docs/troubleshooting/miscellaneous.md +++ b/docs/troubleshooting/miscellaneous.md @@ -1,10 +1,97 @@ --- -status: placeholder -wave: 1 +title: Miscellaneous Troubleshooting +description: Catch-all for issues that don't fit the other troubleshooting pages. --- -# Miscellaneous +# Miscellaneous Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +## Container restart drops everything (SQLite) + +You're using `ROMM_DB_DRIVER=sqlite` (or no `ROMM_DB_DRIVER` at all) and the DB isn't persisted across restarts. + +Mount the database file to persistent storage: + +```yaml +volumes: + - /path/to/database:/romm/database +``` + +This only applies to SQLite. MariaDB / MySQL / Postgres users persist via the DB container's own volume (`mysql_data:/var/lib/mysql` and similar), not via a `/romm/database` mount. + +For anything multi-user or larger than a few hundred games, **don't use SQLite** — see [Databases](../install/databases.md). + +## `Could not get twitch auth token: check client_id and client_secret` + +Your IGDB credentials are wrong or revoked. Full fix in [Authentication Troubleshooting → IGDB](authentication.md#error-could-not-get-twitch-auth-token-check-client_id-and-client_secret). + +## Viewing RomM logs + +Whatever your deployment, the first debugging step is looking at the logs. + +### Docker Compose + +```sh +docker logs -f romm # live tail +docker logs --tail 200 romm # last 200 lines +``` + +### Kubernetes + +```sh +kubectl logs -n romm deploy/romm -f +kubectl logs -n romm deploy/romm --tail=200 +``` + +### Unraid / Synology / TrueNAS + +Logs are available from the GUI container view, or via SSH using the Docker commands above. + +### What to grep for + +Most lines start with `INFO`, `WARNING`, or `ERROR`. Useful filters: + +```sh +docker logs romm 2>&1 | grep ERROR +docker logs romm 2>&1 | grep -iE 'auth|oidc|oauth' +docker logs romm 2>&1 | grep -iE 'scan|metadata' +docker logs romm 2>&1 | grep -iE 'database|redis' +``` + +## Container works, UI is blank / spinner forever + +Open devtools → Network tab → reload. You're looking for: + +- **404 on `/assets/index-*.js`** — nginx inside the container is misrouting. Restart the container. +- **WebSocket connection failed** → reverse proxy isn't forwarding WebSockets. See [Authentication Troubleshooting → WebSockets](authentication.md#400-bad-request-on-the-websocket-endpoint). +- **CORS error** → `ROMM_BASE_URL` doesn't match the URL you're actually accessing. Set it correctly and restart. + +## Covers and screenshots don't load + +Static media is served from `/romm/resources` through nginx. If images 404: + +1. **Mount is wrong** — `docker exec romm ls /romm/resources` should show cached files after at least one scan. +2. **Permissions** — same fix pattern as [Synology permissions](synology.md). +3. **Scan hasn't run yet** — covers are fetched during scan. Run one. + +## RomM is slow + +Most common causes, in order: + +1. **Hashing large files on spinning disks.** Check **Skip hash calculation** or move the library to SSD. +2. **SQLite with many users.** Switch to MariaDB or Postgres — see [Databases](../install/databases.md). +3. **Metadata provider rate limiting during scans.** Reduce `SCAN_WORKERS` or split scans by platform. +4. **Container on underpowered hardware.** Check CPU/RAM headroom with `docker stats romm`. If RAM is pegged, raise container limits or disable `WATCHER_ENABLED`. + +## Upgrade broke something + +1. Roll back the image tag to the version you were on. +2. Restore your DB dump from [Backup & Restore](../install/backup-and-restore.md). +3. Open a bug report with: old version, new version, the exact error or behaviour, and `docker logs romm` from the failed startup. + +For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md) — the migration guide covers every known breaking change. + +## It's not listed here + +- [Discord](https://discord.gg/romm) `#help` — active, friendly, fast. +- [GitHub Issues](https://github.com/rommapp/romm/issues) — for reproducible bugs. +- Include your RomM version, deployment method, logs (secrets redacted), and exact repro steps. diff --git a/docs/troubleshooting/scanning.md b/docs/troubleshooting/scanning.md index 232cf044..baee0186 100644 --- a/docs/troubleshooting/scanning.md +++ b/docs/troubleshooting/scanning.md @@ -1,10 +1,128 @@ --- -status: placeholder -wave: 1 +title: Scanning Troubleshooting +description: Diagnose and fix library-scan problems. --- -# Scanning +# Scanning Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Most scan problems boil down to: library not mounted where RomM expects, folder structure wrong, or a metadata provider being unreachable. + +## Scan ends instantly / no platforms found + +Three common causes: + +1. **Library mounted in the wrong place.** RomM expects your library at `/romm/library` inside the container. Verify with `docker exec romm ls /romm/library` — you should see your `roms/` directory (Structure A) or platform folders (Structure B). +2. **Permissions.** The RomM container has to read the files. Check from the host: `ls -lh /path/to/library`. If the RomM process can't see them, nothing scans. +3. **Invalid folder structure.** Review [Folder Structure](../getting-started/folder-structure.md). RomM tries Structure A first; if it finds neither layout, the scan completes in seconds with nothing found. + +## "ROMs not found for platform X, check romm folder structure" + +Same root cause as the previous one. RomM needs a `roms/` directory somewhere — either `/romm/library/roms/{platform}/` (Structure A) or `/romm/library/{platform}/roms/` (Structure B). + +Common mount-path mistakes: + +```yaml +# WRONG — mounts the platform folder itself +- /server/media/games/snes:/romm/library + +# WRONG — skips the roms/ layer entirely +- /server/media/games:/romm/library + +# CORRECT — mounts the parent of roms/ +- /server/media/library:/romm/library # if library/ contains roms/ +- /server/media/games/roms:/romm/library/roms # if you want to be explicit +``` + +## Platform folder isn't detected + +RomM matches your platform folder names against IGDB platform slugs. If your folder is named differently, it won't match. + +Example: Nintendo 64DD's IGDB slug is `64dd` ([igdb.com/platforms/64dd](https://www.igdb.com/platforms/64dd)), so the folder should be named `64dd/`. If your folder is `n64dd/` or `nintendo-64dd/`, it won't detect. + +Two fixes: + +- **Rename the folder** to match the IGDB slug. +- **Add a binding** in `config.yml` to map your custom name onto the canonical slug: + + ```yaml + system: + platforms: + n64dd: "64dd" # your-folder-name: canonical-slug + ``` + +Full list of supported slugs: [Supported Platforms](../platforms/supported-platforms.md). + +## Scan times out at ~4 hours (or whatever `SCAN_TIMEOUT` says) + +Scans are capped. If yours hits the cap: + +1. **Use `Quick` mode** from the Scan page — skips already-catalogued files. Most repeated scans complete in minutes. +2. **Raise the cap** if you need a full rescan: `SCAN_TIMEOUT_HOURS=8` (or whatever). See [Environment Variables](../reference/environment-variables.md). +3. **Run scans per-platform** instead of everything at once, to checkpoint progress. + +## Scan stops mid-platform + +Check logs: + +```sh +docker logs romm 2>&1 | grep -E 'ERROR.*scan_handler' +``` + +Common culprits: + +- **Corrupted file** — unzip it, re-zip, try again. +- **Old DOS zip with backslash paths** — Python's `zipfile` chokes on these. Re-create the archive with forward slashes. +- **Read errors on the mount** — usually SMB/NFS flakiness. Watch `dmesg` for mount drops. + +Log lines look like: + +```text +ERROR: [RomM][scan_handler][2025-04-12 11:48:55] Failed to process /romm/library/roms/dos/game.zip: ... +``` + +## Tracking progress on a big library + +When scans run for hours across many platforms, the live UI log is hard to follow. Tail the container logs instead: + +```sh +docker logs romm 2>/dev/null | grep -E 'scan_handler.*Identified as' +``` + +Sample output: + +```text +INFO: [RomM][scan_handler][2025-04-12 11:37:40] Identified as PlayStation 🎮 +INFO: [RomM][scan_handler][2025-04-12 14:39:32] Identified as DOS 🎮 +INFO: [RomM][scan_handler][2025-04-13 12:50:42] Identified as WonderSwan 🎮 +``` + +## Metadata provider errors + +Check the scan log for per-provider errors: + +- **IGDB: "Could not get twitch auth token"** → `IGDB_CLIENT_ID` or `IGDB_CLIENT_SECRET` is wrong, or revoked on the Twitch side. +- **ScreenScraper: "403 / DAILY_LIMIT_REACHED"** → you've hit the free-tier quota. Wait, or upgrade your ScreenScraper membership. +- **RetroAchievements: "Invalid API key"** → regenerate at [retroachievements.org/settings](https://retroachievements.org/settings). +- **Any provider: "Request timed out"** → transient; re-run an **Unmatched** scan to retry only the failures. + +Full provider reference: [Metadata Providers](../administration/metadata-providers.md). + +## Lots of ROMs are unmatched + +Options, in order of effort: + +1. **Add more providers.** A ROM IGDB doesn't know about might be in ScreenScraper, Hasheous, or LaunchBox. Enable one or more, then run an **Unmatched** scan. +2. **Use filename tags.** If you already know the provider ID, rename the file to include `(igdb-1234)` or similar. See [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). +3. **Manually match.** Open the ROM detail page, click the **Match** button, and search for the right title. + +## Hash calculations are slow + +Hashing large ROMs (PS1, Saturn, DC images) is IO-bound. Options: + +- **Skip hashing on small hosts** — check **Skip hash calculation** on the Scan page, or set `filesystem.skip_hash_calculation: true` in `config.yml`. You lose RetroAchievements and Hasheous matching (both depend on hashes). +- **Use SSD/NVMe for the library** if you care about hash performance. +- **Raise `SEVEN_ZIP_TIMEOUT`** if you're scanning many large `.7z` archives. + +## Scan seems to work but nothing shows up + +Refresh the page — the ribbons on the home dashboard cache aggressively during a scan. Still blank? Check **Administration → Server Stats** — if counts are zero, the scan didn't actually persist anything. Usually a DB permission issue; check `docker logs romm 2>&1 | grep -i 'database'`. diff --git a/docs/troubleshooting/synology.md b/docs/troubleshooting/synology.md index ca418eb9..bee1f670 100644 --- a/docs/troubleshooting/synology.md +++ b/docs/troubleshooting/synology.md @@ -1,10 +1,67 @@ --- -status: placeholder -wave: 1 +title: Synology Troubleshooting +description: Fix DSM-specific permission and Docker issues. --- -# Synology +# Synology Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +## `ErrNo 13: Access Denied` in Portainer or Container Manager + +The usual Synology permission issue. Fix via SSH: + +1. **Enable SSH** on the NAS if you haven't — [DSM guide](https://kb.synology.com/en-uk/DSM/tutorial/How_to_login_to_DSM_with_root_permission_via_SSH_Telnet). +2. **SSH in** with your DSM admin account (same credentials as the DSM web UI). +3. **Find your UID/GID** — type `id` and note `uid=NNNN(user) gid=NNNN(group)`. +4. **Fix permissions on every RomM host path**: + + ```sh + sudo chown -R : /path/to/library + sudo chmod -R a=,a+rX,u+w,g+w /path/to/library + + sudo chown -R : /path/to/assets + sudo chmod -R a=,a+rX,u+w,g+w /path/to/assets + + sudo chown -R : /path/to/config + sudo chmod -R a=,a+rX,u+w,g+w /path/to/config + ``` + + The paths are whatever you mounted into the container as `/romm/library`, `/romm/assets`, and `/romm/config`. + +5. **Restart the containers**. `docker compose restart` or click Restart in Container Manager. + +Scans should now complete cleanly. + +!!! tip + If you're still getting permission errors *inside* the container after this, the UID/GID running inside the container doesn't match the host files. Either change the container's `user:` directive to match your Synology user, or change the host file ownership to match the container's expected UID. + +Credit: the permission-mode string comes from [DrFrankenstein's Docker user guide](https://drfrankenstein.co.uk/step-2-setting-up-a-restricted-docker-user-and-obtaining-ids/). + +## DSM's built-in MariaDB conflicts with RomM's + +Synology ships its own MariaDB on port `3306`. If you try to run RomM's MariaDB container on the same port, one of them won't bind. + +Fix: map MariaDB to a different host port in your compose file (the Synology install guide uses `3309:3306`) — see [Synology install guide](../install/synology.md). + +## RomM "Page not found" on first open + +First-run takes a few minutes on a NAS — DB initialisation, migrations, static asset seeding. Tail the logs: + +```sh +sudo docker compose logs -f +``` + +Wait until you see RomM report it's listening. Refresh the page. + +## Volume drift after DSM updates + +DSM occasionally rotates Docker's data directory or the BTRFS subvolume paths after an update. Symptom: the container starts but all your data is gone. + +Fix: + +1. Don't panic — your host-mounted paths (`/volume1/...`) aren't touched. +2. Check your compose file. Any volumes declared as *named* Docker volumes (as opposed to host bind mounts) might have been recreated empty. +3. Prefer host bind mounts on Synology specifically — `/volume1/docker/romm/resources` instead of a named `romm_resources` volume. The [Synology install guide](../install/synology.md) uses this pattern. + +## Still stuck + +DSM + RomM issues are best asked in the [RomM Discord](https://discord.gg/romm) `#synology` channel — plenty of users there have seen the same issues and know the Synology quirks. From 4a49892a16762ee3b7053fc3dd5438871fb2f1d5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 17:44:00 +0000 Subject: [PATCH 009/121] docs: complete Wave 1 administration section (4 niche pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes out the Administration section — all 12 admin pages now drafted. - administration/server-stats.md: what the stats page reports, storage-footprint table, per-platform drilldown notes, capacity rule-of-thumb ratios, API access, troubleshooting - administration/observability.md: log levels + grep recipes, /api/heartbeat contract for uptime monitors, Sentry DSN (with what is / isn't sent), OpenTelemetry config (traces/metrics/logs via OTLP), /api/tasks/status scraping, minimum recommended stacks for homelab vs serious deployments - administration/firmware-management.md: two ingest paths (fs drop + scan vs UI upload), per-platform firmware table (PSX, GBA, Sega CD, Saturn, DS), delete-removes-from-disk warning, API surface, integration with in-browser play and companion apps - administration/ssh-sync.md: when SSH sync is actually needed (Grout on muOS / NextUI / Batocera — not HTTPS+token clients), dedicated-key provisioning, container bind mount, device pairing flow, troubleshooting (permission denied, host-key verification), security notes on key rotation and unprivileged device users Builds clean with --strict. --- docs/administration/firmware-management.md | 92 +++++++++++- docs/administration/observability.md | 158 ++++++++++++++++++++- docs/administration/server-stats.md | 84 ++++++++++- docs/administration/ssh-sync.md | 112 ++++++++++++++- 4 files changed, 426 insertions(+), 20 deletions(-) diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index 6769bd5e..e2c34492 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -1,10 +1,92 @@ --- -status: placeholder -wave: 1 +title: Firmware Management +description: Upload, associate, and serve BIOS/firmware files for emulation. --- # Firmware Management -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Many emulated platforms require BIOS or firmware to boot — PlayStation (SCPH1001), Saturn, Game Boy Advance, Sega CD, and more. RomM tracks firmware files **per platform**, stores them on disk, and serves them to in-browser players (EmulatorJS / Ruffle) and companion apps that request them. + +Firmware is **not** ROM. Keep the two separate: + +- `/romm/library/roms/` — games you own dumps of. +- `/romm/library/bios/` (Structure A) or `/romm/library/{platform}/bios/` (Structure B) — system firmware. + +Legality varies by jurisdiction. RomM does not ship firmware and the project cannot help you obtain it. + +## Two paths to ingest firmware + +### Path 1 — drop it in the folder, let the scan pick it up + +The usual workflow if you have firmware on disk already. + +1. Put the file in the right `bios/` folder (Structure A or B — see [Folder Structure](../getting-started/folder-structure.md)). +2. Run a scan. Firmware is picked up alongside ROMs. +3. It shows up in **Administration → Library Management → Firmware**. + +### Path 2 — upload through the UI + +When you don't have shell access or you're uploading from a different machine. + +1. Go to the platform detail page (click a platform card from the home dashboard). +2. Click the **Upload** menu → **Upload Firmware**. +3. Drag and drop the file, or click to browse. +4. RomM puts it in the correct `bios/{platform}/` directory on disk. + +Editors and Admins can upload firmware; Viewers can't (`firmware.write` scope required — see [Users & Roles](users-and-roles.md)). + +## Platform-specific firmware + +Every emulator has its own requirements — which files it needs, specific hashes, naming conventions. The [Supported Platforms](../platforms/supported-platforms.md) table flags which platforms need firmware for EmulatorJS playback; a dedicated [Firmware by Platform](../platforms/firmware-by-platform.md) page lists what's needed for the popular ones. + +Common examples: + +| Platform | Typical firmware file | Where to put it | +| --- | --- | --- | +| PlayStation (PSX) | `scph1001.bin`, `scph5501.bin`, `scph5502.bin` | `bios/ps/` | +| Game Boy Advance | `gba_bios.bin` | `bios/gba/` | +| Sega CD | `bios_CD_U.bin`, `bios_CD_E.bin`, `bios_CD_J.bin` | `bios/segacd/` | +| Saturn | `saturn_bios.bin`, `mpr-17933.bin` | `bios/saturn/` | +| Nintendo DS | `firmware.bin`, `bios9.bin`, `bios7.bin` | `bios/nds/` | + +File naming matters — emulators look for specific filenames. Double-check against the emulator core's documentation if something won't boot. + +## Listing and deleting firmware + +**Administration → Library Management → Firmware** shows every firmware file RomM knows about, grouped by platform. From here you can: + +- See file sizes and detected platform. +- Download a firmware file (useful for verifying integrity off-host). +- Delete a firmware entry. + +!!! warning "Deleting firmware" + Deleting from the UI **does** remove the file from disk. If you want to keep the file but unlink it from RomM, move it out of the `bios/` folder first, then rescan. + +## API + +Firmware has a standard REST surface under `/api/firmware/`: + +- `GET /api/firmware` — list (optional `?platform_id=` filter). +- `POST /api/firmware` — upload. +- `GET /api/firmware/{id}/content/{filename}` — download. +- `POST /api/firmware/delete` — bulk delete. + +Requires `firmware.read` / `firmware.write` scopes. See the [API Reference](../developers/api-reference.md). + +## Integration with in-browser play + +When a user launches a platform that requires firmware in EmulatorJS: + +1. The player checks RomM for a matching firmware file. +2. If present, it's served directly — no user action required. +3. If missing, the player surfaces an error — "firmware required" — and the admin needs to upload one. + +Configure which emulator settings to expose to users via `emulatorjs.settings` in [`config.yml`](../reference/configuration-file.md). Details on the player side in [In-Browser Play](../using/in-browser-play.md). + +## Integration with companion apps + +Handheld syncers (Grout, Argosy, DeckRommSync) can pull firmware alongside ROMs. They use the same Client API Token auth as for ROMs — scope the token to include `firmware.read`. See [Client API Tokens](../ecosystem/client-api-tokens.md). + +## Backups + +Firmware is user-owned data — back it up. `/romm/library/bios/` is part of your library mount; if you're backing up the library volume, firmware is already covered. See [Backup & Restore](../install/backup-and-restore.md). diff --git a/docs/administration/observability.md b/docs/administration/observability.md index 6fe99d53..097a5964 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -1,10 +1,158 @@ --- -status: placeholder -wave: 1 +title: Observability +description: Logs, Sentry error tracking, OpenTelemetry, and the /heartbeat endpoint. --- # Observability -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Four ways to know what RomM is doing: + +- **Container logs** — always available, the first stop. +- **`/api/heartbeat`** — health + config summary for uptime monitors. +- **Sentry** — opt-in error tracking with stack traces. +- **OpenTelemetry** — opt-in distributed tracing + metrics. + +## Logs + +### Log levels + +```yaml +environment: + - LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR + - FORCE_COLOR=0 # 1 to force colour even when not a TTY + - NO_COLOR=1 # 1 to disable colour entirely +``` + +`INFO` is the default and sane for production. Drop to `DEBUG` only while debugging a specific issue — RomM is chatty on DEBUG. + +### Reading logs + +Log lines are prefixed with module + timestamp: + +```text +INFO: [RomM][scan_handler][2026-04-18 11:37:40] Identified as PlayStation 🎮 +ERROR: [RomM][ra_handler][2026-04-18 11:48:55] Invalid RetroAchievements API key +WARNING: [RomM][config_manager][2026-04-18 12:01:12] config.yml not found, using defaults +``` + +Useful greps: + +```sh +docker logs romm 2>&1 | grep ERROR +docker logs romm 2>&1 | grep -iE 'auth|oidc|oauth' +docker logs romm 2>&1 | grep -iE 'scan_handler.*Identified' +``` + +Deployment-specific log commands are in [Miscellaneous Troubleshooting → Viewing RomM logs](../troubleshooting/miscellaneous.md#viewing-romm-logs). + +## `/api/heartbeat` + +A single-request health + config endpoint. Safe to hit anonymously (though some fields only appear for authenticated callers). + +```http +GET /api/heartbeat +``` + +Returns: + +- RomM version. +- Whether the Setup Wizard is still pending. +- Which metadata providers are enabled. +- Which platforms have data. +- OIDC config (redacted credentials). +- Scheduled-task schedule summary. +- Watcher status. + +Wire this to your uptime monitor. A failure here is real — the process is down or the DB/Redis is unreachable. + +```bash +# Basic uptime check +curl -fsS https://romm.example.com/api/heartbeat > /dev/null \ + && echo "up" \ + || echo "down" +``` + +Per-metadata-provider health: + +```http +GET /api/heartbeat/metadata/igdb +GET /api/heartbeat/metadata/ss +GET /api/heartbeat/metadata/ra +... +``` + +Useful when a scan is matching poorly and you want to know whether a provider is down on their side or misconfigured on yours. + +## Sentry + +Opt-in error tracking. Nothing is sent without a DSN. + +```yaml +environment: + - SENTRY_DSN=https://abc123@sentry.example.com/42 +``` + +What's sent: + +- Stack traces for unhandled exceptions. +- Per-request timing on slow endpoints. +- Redacted URL parameters (secrets stripped). + +What's not sent: ROM filenames, user credentials, metadata provider API keys. RomM filters sensitive parameters before reporting. + +Suitable for self-hosted Sentry or [sentry.io](https://sentry.io/). Drop the DSN to stop reporting. + +## OpenTelemetry + +Opt-in distributed tracing and metrics. Useful if you run RomM alongside other services and want unified observability. + +```yaml +environment: + - OTEL_ENABLED=true + - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 + - OTEL_SERVICE_NAME=romm + - OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod +``` + +Standard [OTEL env vars](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) apply. RomM emits: + +- **Traces** — HTTP request spans, DB query spans, RQ job spans. +- **Metrics** — request counts, durations, queue depth, scan progress. +- **Logs** — structured log correlation with trace IDs. + +Exporters: + +- OTLP gRPC (default, port `4317`). +- OTLP HTTP (port `4318`) — set `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`. + +Send to an OpenTelemetry Collector, then fan out to Tempo / Jaeger / Honeycomb / Datadog / Grafana Cloud / whatever you run. + +## Task status + +Live task state is also exposed programmatically: + +```http +GET /api/tasks/status +Authorization: Bearer +``` + +Returns an array of every scheduled / manual / watcher task with current status (`idle`, `queued`, `running`, `failed`) and last run time. Scrape this into your monitoring to alert on "Folder Scan hasn't run in 48 hours" — which usually means RQ workers are dead. + +## Anti-patterns + +- **Don't parse unstructured log lines** for metrics. Use OTEL or `/api/tasks/status`. +- **Don't log at DEBUG in production.** The volume is real and scans will drown in it. +- **Don't scrape HTML pages for health checks.** `/api/heartbeat` is the contract — HTML changes between versions, the API endpoint is stable. + +## Minimum recommended stack + +For a homelab instance: + +- Default `INFO` logs into the container logs → forwarded to Loki / Promtail / whatever you already run. +- `/api/heartbeat` hit every 60 seconds from Uptime Kuma / Gatus. + +For a serious deployment: + +- Above, plus Sentry DSN configured. +- Plus OpenTelemetry to the collector you already have. +- Alert on: heartbeat failure, task stuck > 1 h, `ERROR`-level log spikes. diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 4a499154..6543ac3d 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -1,10 +1,84 @@ --- -status: placeholder -wave: 1 +title: Server Stats +description: Read the Server Stats admin page and know what the numbers mean. --- # Server Stats -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +**Administration → Server Stats.** Admin-only page that reports what's on disk and in the catalogue. Useful for capacity planning, spotting a runaway platform, and verifying a scan actually persisted data. + +## What's shown + +### Top-line counts + +| Metric | What it counts | +| --- | --- | +| **Platforms** | Every platform RomM has seen at least one ROM for. Deleted platforms don't count. | +| **Games** | Total ROM entries. A multi-file game (folder with multiple files) counts as 1. | +| **Saves** | User save files across all users. | +| **States** | Emulator save states across all users. | +| **Screenshots** | User-uploaded screenshots. Provider-fetched screenshots aren't counted here. | +| **Firmware** | Uploaded BIOS / firmware files. | + +### Storage footprint + +A breakdown of disk usage by directory: + +| Bucket | Maps to | Grows when | +| --- | --- | --- | +| **Library** | `/romm/library` | You add ROMs. Controlled by you, not RomM. | +| **Resources** | `/romm/resources` | Scans fetch cover art, screenshots, manuals. Safe to purge — can be rebuilt from a rescan. | +| **Assets** | `/romm/assets` | Users upload saves, states, screenshots. **Back this up.** | +| **Config** | `/romm/config` | Rarely — you or admins edit `config.yml`. | + +### Per-platform breakdown + +Under the summary, an expandable table sorted by either ROM count or disk usage. Click a platform to drill into: + +- Matched / unmatched count. +- Region distribution (how many games tagged USA, Japan, Europe, World, etc.). +- Language distribution. +- Metadata coverage — how many games have each field populated (cover, description, release date, rating). + +Useful for: "which platform is eating my disk?" and "which platform has the worst match rate?" + +!!! note "Per-platform stats are opt-in" + Computing per-platform stats walks the whole DB and costs real time on big libraries. The main stats load fast; per-platform expansion loads on demand. + +## What the numbers don't include + +- **Disk usage for the database itself** — MariaDB / Postgres data volume isn't reported here. Use `docker system df -v` or your volume backend's native tooling. +- **Redis memory** — same; monitor Redis separately if you're tight on RAM. +- **Per-user storage breakdown** — not exposed in 5.0. + +## Using stats for capacity planning + +Rule-of-thumb sizes: + +| Ratio | Typical value | +| --- | --- | +| Resources / Library | 2-5% on average; higher if you've enabled many metadata providers or run Image Conversion often. | +| Assets / Library | <1% unless you have lots of heavy PSP/PS3 saves. | +| DB size / Games | ~10-50 KB per ROM row, depending on metadata richness. | + +If **Resources** is ballooning, run the [Cleanup Orphaned Resources](scheduled-tasks.md#cleanup-orphaned-resources-manual) task. If **Assets** is growing unexpectedly, a user is probably uploading save states for long-form JRPGs — that's fine, just plan for it. + +## API + +The same data is available programmatically: + +```http +GET /api/stats +GET /api/stats?include_platform_stats=true +Authorization: Bearer +``` + +Wire to your monitoring stack via the API rather than scraping the HTML page. See the [API Reference](../developers/api-reference.md). + +## Troubleshooting + +- **Numbers look stale** — stats are computed on page load, not cached. Reload. If still stale, the DB connection is degraded; check `docker logs romm 2>&1 | grep -i database`. +- **Disk sizes look wrong** — RomM reports what it sees in `/romm/*`. If your compose mounts a path that's smaller than the host dataset (e.g. you mounted a sub-directory), RomM only sees that subset. +- **"Platform stats couldn't load"** — the DB query timed out. On very large libraries this happens; retry, or raise `SCAN_TIMEOUT_HOURS` (yes, it also gates the stats query). + +For anything else: [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/administration/ssh-sync.md b/docs/administration/ssh-sync.md index dd0492ba..b59d2161 100644 --- a/docs/administration/ssh-sync.md +++ b/docs/administration/ssh-sync.md @@ -1,10 +1,112 @@ --- -status: placeholder -wave: 1 +title: SSH Sync +description: Configure SSH key-based push/pull sync to handhelds and other devices. --- # SSH Sync -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM's Push-Pull Device Sync task can push saves/states to registered devices and pull them back after a session — over SSH, using a key that RomM holds. This page covers the server-side setup. + +The client side — a handheld running Grout, a SteamDeck running DeckRommSync, etc. — lives in [Integrations & Ecosystem](../ecosystem/index.md). The wire protocol (API-level sync negotiation, play-session ingest) lives in [Device Sync Protocol](../ecosystem/device-sync-protocol.md). + +!!! note "Most companion apps don't need this" + Argosy, Playnite, and most mobile/desktop clients sync via HTTPS + Client API Tokens. SSH sync is specifically for handhelds and similar devices where RomM pushes files to a filesystem the device exposes via SSH — Grout on muOS / NextUI being the canonical case. + +## When you need SSH sync + +- A handheld running custom firmware exposing SSH (muOS, NextUI, Batocera, etc.). +- You want RomM to automatically copy saves/states to the device and pull them back when a session ends. +- Your sync runs on a schedule, not on-demand. + +If none of that applies, you don't need this page — HTTPS + Client API Tokens is simpler and more flexible. + +## Configuring the server + +### 1. Provision an SSH key for RomM + +Generate a dedicated key on the host that runs the RomM container: + +```sh +ssh-keygen -t ed25519 -f ~/romm-sync-key -N "" -C "romm-sync" +``` + +This produces `~/romm-sync-key` (private) and `~/romm-sync-key.pub` (public). Keep the private key readable only by you: `chmod 600 ~/romm-sync-key`. + +### 2. Mount the key into the RomM container + +```yaml +services: + romm: + volumes: + - /home/you/romm-sync-key:/romm/.ssh/id_rsa:ro + environment: + - SSH_PRIVATE_KEY_PATH=/romm/.ssh/id_rsa +``` + +Keep it read-only. RomM doesn't need to modify the key. + +`SSH_PRIVATE_KEY_PATH` can point anywhere inside the container — conventionally `/romm/.ssh/id_rsa`. + +### 3. Restart RomM + +```sh +docker compose up -d +``` + +The Push-Pull Device Sync task will now use that key for outbound SSH connections. + +## Configuring a device + +For each handheld / device you want to sync: + +### 1. Authorise the RomM key + +Copy the **public** key (`~/romm-sync-key.pub` from step 1 above) to the device's `~/.ssh/authorized_keys`. Exactly how depends on the device — Grout on muOS has a helper, others expose a filesystem you can `ssh-copy-id` into. + +### 2. Register the device in RomM + +Device registration is done through a companion app (typically Grout itself) using the [Client API Token pairing flow](../ecosystem/client-api-tokens.md). Once registered, RomM knows: + +- Device name + type. +- Hostname / IP. +- SSH port (default `22`). +- Target paths on the device for saves, states, and any other synced assets. + +### 3. Test the connection + +From the RomM container, confirm SSH works: + +```sh +docker exec romm ssh -i "$SSH_PRIVATE_KEY_PATH" user@ echo ok +``` + +You should see `ok`. If you see a host-key prompt, accept it — RomM will remember it in its `known_hosts`. If you see `permission denied`, the authorised key isn't installed correctly. + +## How sync runs + +The **Push-Pull Device Sync** scheduled task (default: every 15 minutes) does, for each registered device: + +1. Connects via SSH. +2. Lists saves/states on the device and compares against RomM's DB. +3. Uploads (push) anything the device is missing. +4. Downloads (pull) anything newer on the device. +5. Updates play session records if the device reports any. + +Tune the schedule via `PUSH_PULL_SYNC_INTERVAL_CRON`. See [Scheduled Tasks](scheduled-tasks.md). + +Disable sync for a specific device by deregistering it from **Administration → Devices**, or disable the task entirely by unsetting / neutering `PUSH_PULL_SYNC_INTERVAL_CRON`. + +## Troubleshooting + +- **`Permission denied (publickey)`** — authorised key isn't set up on the device, or the private key inside the container can't be read (check the file permissions and bind-mount flags). +- **`Host key verification failed`** — the device's host key changed (after a reinstall, typically). Shell into the container and remove the offending line from `~/.ssh/known_hosts`. +- **Sync silently doesn't run** — check `GET /api/tasks/status` for the Push-Pull task's state. "failed" points you at the error; "never ran" means the cron isn't firing (see [Scheduled Tasks](scheduled-tasks.md)). +- **Connection times out** — the device is offline or the network path is blocked. Confirm reachability from the RomM container: `docker exec romm ping `. + +More at [Device Sync Troubleshooting](../troubleshooting/sync.md). + +## Security notes + +- The RomM sync key has read-write access to save paths on every registered device. Compromise = every device's saves gettable. Don't reuse this key for anything else. +- Rotate the key periodically: regenerate, update the volume mount, re-authorise on each device. +- Use a dedicated unprivileged user on the device (not root), with write access scoped only to the save directories. From 9e7527c1cc3aa6fbef2beca3d8a5661525050fe8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 17:47:50 +0000 Subject: [PATCH 010/121] docs: draft Wave 1 releases section (4 pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - releases/index.md: versioning policy (SemVer + CalVer-ish cadence), image tag pin guide, support window table, docs-versions pointer to mike's frozen 4.8 tree, pre-upgrade protocol - releases/upgrading-to-3.0.md: ported from Maintenance/. Historical 2.x → 3.0 guide kept for anyone still on 2.x. Link updates for new IA. - releases/upgrading-to-5.0.md: NEW 4.x → 5.0 migration. Clear pre-flight + do-not-skip-steps framing. Env var migration table with all known 5.0 additions. config.yml new-sections list. Docs URL redirect summary (referencing the mkdocs-redirects map in mkdocs.yml). Common-issue runbook (migrations hang, WebSockets, OIDC role demotion, cron var renames). Full rollback procedure. Flagged as "finalised at GA" — specific rename list filled in against the 5.0.0 source tag when cut. - releases/changelog.md: human-readable per-major summary pointing at GitHub Releases as the authoritative changelog. Covers 5.0, rolls up 4.x, keeps 3.0 for context. Builds clean with --strict. --- docs/releases/changelog.md | 61 ++++++- docs/releases/index.md | 75 +++++++- docs/releases/upgrading-to-3.0.md | 95 +++++++++- docs/releases/upgrading-to-5.0.md | 278 +++++++++++++++++++++++++++++- 4 files changed, 489 insertions(+), 20 deletions(-) diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md index f4839d3a..7a4f93cc 100644 --- a/docs/releases/changelog.md +++ b/docs/releases/changelog.md @@ -1,10 +1,61 @@ --- -status: placeholder -wave: 1 +title: Changelog +description: Summary of notable RomM releases; authoritative per-release notes live on GitHub. --- # Changelog -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +This page summarises major RomM releases. **The authoritative per-release changelog lives on [GitHub Releases](https://github.com/rommapp/romm/releases)** — every tag there has full commit lists, breaking changes flagged, and upgrade notes. + +For migration-grade detail on a major version, go straight to that version's guide: + +- **[Upgrading to 5.0](upgrading-to-5.0.md)** +- **[Upgrading to 3.0](upgrading-to-3.0.md)** + +## 5.0 + +[What's New in 5.0](../getting-started/what-is-new-in-5.md) · [Upgrading to 5.0](upgrading-to-5.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/5.0.0) + +The biggest release since the project started. Highlights: + +- **Console Mode** — a gamepad-first `/console` UI for TVs. +- **Smart & Virtual Collections** — rule-based and auto-generated collections. +- **ROM Patcher** — apply IPS/UPS/BPS/PPF and more in-browser. +- **Netplay** — EmulatorJS multiplayer with ICE servers. +- **Device sync protocol** + Client API Tokens with device pairing. +- **OIDC role mapping** via claims (`OIDC_CLAIM_ROLES`). +- **Thirteen metadata providers** — added TheGamesDB, Libretro, gamelist.xml importer. +- **PWA install** support. +- **19 locales**. +- **OpenTelemetry** export via `OTEL_ENABLED`. +- **Slim + Full image variants**. + +## 4.x + +Highlights rolled up: + +- **4.8** — stability and per-platform stats opt-in. +- **4.x** — iterative improvements on the 3.x → 4.x baseline: expanded metadata sources, frontend polish, first-class companion-app support. + +The 4.x docs live at [docs.romm.app/4.8/](https://docs.romm.app/4.8/) as a frozen snapshot. See [Release Notes & Migration → Docs versions](index.md#docs-versions). + +## 3.0 + +[Upgrading to 3.0](upgrading-to-3.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/3.0.0) + +Watershed release that made RomM a real multi-user platform: + +- **Required authentication** — no more unauthenticated mode. +- **SQLite support dropped** — MariaDB became the default. +- **Redis built-in** — the experimental Redis add-on was absorbed. +- **Saves, states, and screenshots** — first-class user asset management. +- **Config moved to a folder mount** (`/romm/config`). + +## Older + +2.x and earlier aren't supported and aren't covered by migration guides. Upgrade to 3.x using the 3.0 guide, then to 5.x using the 5.0 guide. + +## Want release pings? + +- Watch the [rommapp/romm](https://github.com/rommapp/romm) repo on GitHub — "Custom → Releases" for release-only notifications. +- Join the [Discord](https://discord.gg/romm) and look for `#announcements`. diff --git a/docs/releases/index.md b/docs/releases/index.md index 1b494f98..4bde2557 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -1,10 +1,75 @@ --- -status: placeholder -wave: 1 +title: Release Notes & Migration +description: RomM release cadence, versioning policy, docs versions, and per-major migration guides. --- # Release Notes & Migration -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +## Current version + +RomM's current stable is [`5.0.0`](changelog.md). Read [What's New in 5.0](../getting-started/what-is-new-in-5.md) for the highlights, or [Upgrading to 5.0](upgrading-to-5.0.md) if you're coming from 4.x. + +## Versioning + +RomM follows **SemVer** for breaking changes and **CalVer-ish** for cadence — major versions are planned milestones, not scheduled. + +- **Patch releases** (`5.0.1`, `5.0.2`) — bug fixes only. Safe to pull automatically. No migration. +- **Minor releases** (`5.1.0`, `5.2.0`) — additive features. Schema may migrate automatically via Alembic; no action required beyond reading the release notes. +- **Major releases** (`5.0.0`, `6.0.0`) — breaking changes. Read the migration guide before upgrading. + +## Image tags and what to pin + +| Tag | What it moves to | When to use | +| --- | --- | --- | +| `rommapp/romm:latest` | Every new stable release | Dev / "I'll deal with it" setups. **Never pin production to `:latest`** — you'll ship untested upgrades. | +| `rommapp/romm:5.0.0` | Immutable — specific release | Production. Update deliberately by bumping the tag. | +| `rommapp/romm:5` | Latest in the 5.x line | Middle ground — auto-minor-upgrades within a major. | +| `rommapp/romm:develop` | Every push to `master` | Don't. | +| `rommapp/romm:5.0.0-slim` | Same as `5.0.0` but without EmulatorJS/Ruffle | Headless / API-only deployments. See [Image Variants](../install/image-variants.md). | + +Registries: Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`) — same tags, same content. + +## Docs versions + +The docs site is versioned via [mike](https://github.com/jimporter/mike). Every major RomM release gets its own docs tree, accessible from the version switcher top-left: + +- `docs.romm.app/latest/` → 5.x (this is what you're reading). +- `docs.romm.app/4.8/` → frozen 4.x docs. +- Older majors may remain accessible for a while; see the support window below. + +## Support window + +We support the current major and the previous major for critical bug fixes and security patches. + +| Major | Status | Frozen docs | +| --- | --- | --- | +| **5.x** | **Current** — active development | `docs.romm.app/latest/` | +| **4.x** | Security + critical bugs only | `docs.romm.app/4.8/` | +| **3.x** | Unsupported — upgrade | — | +| **≤2.x** | Unsupported | — | + +Older frozen docs are retained for 12 months after the major's support window ends, then removed. Plan upgrades accordingly. + +## Migration guides + +- **[Upgrading to 5.0](upgrading-to-5.0.md)** — 4.x → 5.0. Required reading. +- **[Upgrading to 3.0](upgrading-to-3.0.md)** — 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical — keep for 2.x migrators. + +## Where releases are announced + +- [GitHub Releases](https://github.com/rommapp/romm/releases) — authoritative changelog, tag-by-tag. +- [Discord](https://discord.gg/romm) `#announcements` — release pings. +- [Changelog](changelog.md) — human-readable per-release summary. + +## Upgrade protocol + +Before pulling a new major: + +1. Read the relevant migration guide. +2. Back up: `mysqldump` / `pg_dump` + `/romm/assets` + `/romm/config`. See [Backup & Restore](../install/backup-and-restore.md). +3. Pull the new image to your host but don't `up -d` yet. +4. In a quiet window, `docker compose down && docker compose up -d`. +5. Watch `docker logs -f romm` for any Alembic migration or startup errors. +6. Verify Server Stats, run a Quick scan, log in as a Viewer, confirm nothing is visibly broken. + +If something breaks, revert the image tag and restore the backup. See the per-guide rollback sections. diff --git a/docs/releases/upgrading-to-3.0.md b/docs/releases/upgrading-to-3.0.md index fff6f8d3..56ea9e7a 100644 --- a/docs/releases/upgrading-to-3.0.md +++ b/docs/releases/upgrading-to-3.0.md @@ -1,10 +1,95 @@ --- -status: placeholder -wave: 1 +title: Upgrading to 3.0 +description: Historical migration guide for 2.x → 3.0 — SQLite drop, auth required, Redis built-in, config folder mount, assets volume. --- # Upgrading to 3.0 -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +!!! info "This is a historical guide" + RomM 3.0 shipped a while ago. If you're on 2.x, still use this page. If you're on 3.x / 4.x, see [Upgrading to 5.0](upgrading-to-5.0.md). + +Version 3.0 introduced several breaking changes aimed at improving performance and unlocking new features (EmulatorJS, in-browser saves/states). Read this entire page before pulling the new image — skipping steps will make RomM inaccessible or unresponsive. + +All the changes below are reflected in the example [docker-compose.yml](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml), which was significantly simplified in 3.0. + +## Dropped SQLite support + +SQLite was removed because of ongoing engineering issues; MariaDB is stabler and the only supported default. If you were on SQLite, RomM will auto-migrate your data to MariaDB — **but you need to make the following changes before upgrading**. + +In your environment variables, set `ROMM_DB_DRIVER` to `mariadb` (or drop the variable entirely — it's no longer required) and add: + +```yaml +environment: + - DB_HOST=mariadb + - DB_PORT=3306 + - DB_NAME=romm # matches MYSQL_DATABASE in mariadb + - DB_USER=romm-user # matches MYSQL_USER + - DB_PASSWD= # matches MYSQL_PASSWORD +``` + +Set up the MariaDB container using the [reference compose](../install/docker-compose.md). + +## Authentication is now required + +To support EmulatorJS, save/state management, and multi-user features, 3.0 requires authentication. If you were running with auth disabled, remove `ROMM_AUTH_ENABLED` and add: + +```yaml +environment: + - ROMM_AUTH_SECRET_KEY= +``` + +We know this breaks the "unrestricted sharing" pattern some users relied on. See [Kiosk Mode](../administration/authentication.md#kiosk-mode) for a read-only anonymous-access option that ships with RomM. + +## Redis is built in + +The 2.x "experimental Redis" add-on container is gone — RomM ships with Redis internally. Remove the external Redis container and these environment variables: + +```yaml +# Remove: +# - ENABLE_EXPERIMENTAL_REDIS +# - REDIS_HOST +# - REDIS_PORT +``` + +If you want an external Redis instance anyway (recommended for production), keep `REDIS_HOST` / `REDIS_PORT` — see [Redis or Valkey](../install/redis-or-valkey.md). + +## Configuration folder + +`config.yml` is now mounted via a **config folder**, not a single file mount. + +Place your existing `config.yml` inside a directory and bind that directory to `/romm/config`: + +```yaml +volumes: + - /path/to/config:/romm/config +``` + +Updated example: [config.example.yml](https://github.com/rommapp/romm/blob/master/examples/config.example.yml). See also [Configuration File](../reference/configuration-file.md). + +## New `/romm/assets` volume + +3.0 introduced save, state, and screenshot management. Uploads land in `/romm/assets` inside the container — mount a host path so they persist: + +```yaml +volumes: + - /path/to/assets:/romm/assets +``` + +Put this next to the folder you mount as `/romm/library` so all RomM-owned data stays together. + +## After the upgrade + +- Visit the web UI. You'll see a Setup Wizard if you had auth disabled previously — complete it to create the first admin. +- Run a scan to re-match any games that lost metadata during the SQLite → MariaDB migration. +- Start using saves/states — they'll live under `/romm/assets` from now on. + +## Rollback + +If something breaks: + +1. Restore your old `docker-compose.yml` / env file. +2. Restore your SQLite DB file backup (if you were on SQLite). +3. Pull the 2.x image tag explicitly (`rommapp/romm:2.x.y`). +4. `docker compose up -d`. + +Going forward, the 3.x / 4.x / 5.x chain of migrations is documented in their respective guides. Start with [Upgrading to 5.0](upgrading-to-5.0.md) once you're on 3.x. diff --git a/docs/releases/upgrading-to-5.0.md b/docs/releases/upgrading-to-5.0.md index 74584654..9229773f 100644 --- a/docs/releases/upgrading-to-5.0.md +++ b/docs/releases/upgrading-to-5.0.md @@ -1,10 +1,278 @@ --- -status: placeholder -wave: 1 +title: Upgrading to 5.0 +description: Migrate from RomM 4.x to 5.0 — breaking changes, env var renames, pre-flight checklist, and rollback. --- # Upgrading to 5.0 -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +Migration guide for **RomM 4.x → 5.0**. If you're on 2.x or earlier, upgrade to 3.x first via [Upgrading to 3.0](upgrading-to-3.0.md), then come back here. + +!!! danger "Read this entire page before upgrading" + 5.0 is a major release with schema migrations and a handful of breaking changes. Skipping the pre-flight checklist will lose data or break your instance. + +!!! note "This guide is finalised at 5.0 GA" + Some sections below reference exact env-var renames and schema changes that will be confirmed against the final 5.0.0 source tag. If you're reading this before 5.0 GA, treat it as a structural outline — the commands are correct, specific rename lists will be filled in as the release tag is cut. + +## What changes in 5.0 + +### Summary + +- **Schema migration** — Alembic runs automatically on startup. Irreversible without a backup. +- **New env vars**, some renamed (see [Env var migration table](#env-var-migration-table)). +- **`config.yml` gains new sections** — `scan.region`, `scan.language`, `scan.media`, `scan.gamelist.export`, `scan.pegasus.export`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings keep working untouched. +- **Image tags add `:slim` variants** — no change to existing `:latest` / `:5.0.0`. +- **Docs URL structure** — every old URL redirects automatically. External links continue working. + +### Big new features + +Highlights only — full list in [What's New in 5.0](../getting-started/what-is-new-in-5.md): + +- Console Mode (`/console` — TV/gamepad UI). +- Smart and Virtual Collections. +- ROM Patcher. +- Netplay with ICE servers. +- PWA install. +- 19 locales. +- Device Sync protocol (bidirectional push-pull for handhelds). +- Client API Tokens with device pairing. +- OIDC role mapping via claims. +- Expanded metadata providers: TheGamesDB, Libretro, gamelist.xml importer. +- OpenTelemetry export. + +## Pre-flight checklist + +**Do not skip.** + +### 1. Read + +- This whole page. +- [What's New in 5.0](../getting-started/what-is-new-in-5.md). +- The release's [GitHub Release notes](https://github.com/rommapp/romm/releases). + +### 2. Check you're on a recent 4.x + +5.0's migrations assume you've already run the 4.x migrations. From a 4.7 or 4.8 baseline is ideal. If you're on 3.x: + +1. Upgrade to the latest 4.x first (`rommapp/romm:4.8.1` at time of writing). +2. Verify the instance works. +3. Then upgrade to 5.0. + +Don't jump 3.x → 5.0 in one step. + +### 3. Back up + +See [Backup & Restore](../install/backup-and-restore.md). At minimum: + +```sh +# MariaDB +docker exec romm-db mariadb-dump --user=romm-user --password=$DB_PASSWD \ + --single-transaction --databases romm > romm-db-pre-5.0.sql + +# or Postgres +docker exec romm-db pg_dump --username=romm-user --dbname=romm > romm-db-pre-5.0.sql + +# Assets + config +rsync -a /path/to/assets/ /backup/romm/assets-pre-5.0/ +rsync -a /path/to/config/ /backup/romm/config-pre-5.0/ +``` + +Don't skip this. "It's a minor upgrade" isn't a thing for a major version. + +### 4. Pin the current version + +Before pulling 5.0, change your `docker-compose.yml` to pin the old version explicitly so you can roll back: + +```yaml +image: rommapp/romm:4.8.1 # whatever you're actually on +``` + +Commit this to version control if you track your compose file. Saves guesswork on rollback. + +### 5. Block access while you upgrade + +Optional but recommended for shared instances — take RomM offline for ~5 minutes: + +```sh +# Return 503 at the reverse proxy while upgrading +``` + +Prevents users hitting mid-migration state. + +## Upgrade steps + +### 1. Pull the new image + +```sh +docker pull rommapp/romm:5.0.0 +``` + +Or pick the slim variant: + +```sh +docker pull rommapp/romm:5.0.0-slim +``` + +### 2. Update your compose file + +Edit `docker-compose.yml`: + +- Change the image tag to `rommapp/romm:5.0.0` (or `:5.0.0-slim`). +- Add any new env vars relevant to your setup from the [migration table](#env-var-migration-table). +- Rename any renamed env vars. +- Double-check volumes are still correct. + +### 3. Bring it up + +```sh +docker compose up -d +docker compose logs -f romm +``` + +What to watch for: + +- **Alembic migrations running** — lines prefixed `INFO [alembic.runtime.migration]`. Takes anywhere from seconds (small libraries) to a couple minutes (very large ones). +- **`Application startup complete.`** — RomM is healthy. +- **`Watcher started.`** — filesystem watcher up (if enabled). +- **ERROR lines** — bad; go to [Rollback](#rollback). + +### 4. Smoke-test + +Don't trust the migration until you've verified: + +- Log in as an Admin. +- **Administration → Server Stats** — counts look reasonable; no massive loss. +- Open a platform with lots of games; check a few games have metadata. +- Open a game's details — Personal tab shows your rating / playtime if you had any. +- Check **Administration → Users** — your accounts are intact. +- Run a **Quick** scan — should complete cleanly in a few seconds. +- (If you had OIDC) log out, log in via OIDC — should pick up `OIDC_CLAIM_ROLES` if configured. + +## Env var migration table + +| 4.x name | 5.0 name | Change | +| --- | --- | --- | +| `SCREENSCRAPER_USER` | `SCREENSCRAPER_USER` | No change. | +| `IGDB_CLIENT_ID` | `IGDB_CLIENT_ID` | No change. | +| _(new in 5.0)_ | `ALLOW_PUBLIC_REGISTRATION` | Opt-in public signup. Off by default. | +| _(new in 5.0)_ | `INVITE_TOKEN_DAYS` | Invite link expiry (days). Default 30. | +| _(new in 5.0)_ | `OIDC_CLAIM_ROLES` | Which claim to read for role mapping. | +| _(new in 5.0)_ | `OIDC_ROLE_VIEWER` / `OIDC_ROLE_EDITOR` / `OIDC_ROLE_ADMIN` | Map OIDC group names to RomM roles. | +| _(new in 5.0)_ | `ENABLE_NETPLAY` | Toggle Netplay (default on). | +| _(new in 5.0)_ | `WATCHER_ENABLED` | Filesystem watcher (default on). | +| _(new in 5.0)_ | `WATCH_EXTENSIONS_ONLY` | Watcher filters by extension. | +| _(new in 5.0)_ | `RESCAN_ON_FILESYSTEM_CHANGE` / `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` | Watcher debounce. | +| _(new in 5.0)_ | `OTEL_ENABLED` | Opt into OpenTelemetry export. | +| _(new in 5.0)_ | `SSH_PRIVATE_KEY_PATH` | Path to SSH key used by Push-Pull Device Sync. | +| _(new in 5.0)_ | `TGDB_API_ENABLED` | Enable TheGamesDB as a metadata source. | +| _(new in 5.0)_ | `RETROACHIEVEMENTS_SYNC_INTERVAL_CRON` / `NETPLAY_CLEANUP_INTERVAL_CRON` / `PUSH_PULL_SYNC_INTERVAL_CRON` / `IMAGE_CONVERSION_INTERVAL_CRON` / `SWITCH_TITLEDB_FETCH_INTERVAL_CRON` | New scheduled-task cron controls. | + +Full list in [Environment Variables](../reference/environment-variables.md). The reference page is autogenerated from the upstream `env.template`, so it stays correct even as more vars land in 5.x patches. + +## `config.yml` changes + +`config.yml` gains new sections; everything else stays backwards compatible. + +- **`scan.region`** — region preference order (array). +- **`scan.language`** — language preference order (array). +- **`scan.media`** — which media types to fetch (`[box2d, screenshot, manual]`, etc.). +- **`scan.gamelist.export`** + **`scan.gamelist.media.*`** — export gamelist.xml for ES-DE/Batocera. +- **`scan.pegasus.export`** — export metadata.pegasus.txt for Pegasus. +- **`filesystem.firmware_folder`** — override the `bios` folder name. +- **`emulatorjs.netplay.ice_servers`** — STUN/TURN for Netplay. +- **`emulatorjs.settings`** / **`emulatorjs.controls`** — per-core emulator config and button mappings. +- **`emulatorjs.cache_limit`** / **`emulatorjs.disable_batch_bootup`** / **`emulatorjs.disable_auto_unload`**. + +Full schema: [Configuration File](../reference/configuration-file.md). + +## Docs URL changes + +The docs site restructured in 5.0. Every old URL redirects to its new home: + +- `/latest/Getting-Started/Quick-Start-Guide/` → `/latest/getting-started/quick-start/` +- `/latest/System-Setup/Synology-Setup-Guide/` → `/latest/install/synology/` +- `/latest/Usage/UserManagement/` → `/latest/administration/users-and-roles/` +- `/latest/OIDC-Guides/OIDC-Setup-With-Keycloak/` → `/latest/administration/oidc/keycloak/` + +And so on — every page in the 4.x IA is covered. External links in forum posts, blog articles, issue threads, and Discord messages keep working. Internal bookmarks to `/latest///` hit a 301 to the new slug automatically. + +## Rollback + +If something's clearly broken after the upgrade: + +### 1. Stop the stack + +```sh +docker compose down +``` + +### 2. Revert the image tag + +Edit `docker-compose.yml` — change `rommapp/romm:5.0.0` back to whatever you were on (`rommapp/romm:4.8.1`). + +### 3. Restore the DB + +```sh +# MariaDB / MySQL +docker compose up -d romm-db # DB only +docker exec -i romm-db mariadb --user=root --password=$ROOT_PW romm \ + < romm-db-pre-5.0.sql + +# Postgres +docker compose up -d romm-db +docker exec -i romm-db psql --username=romm-user --dbname=romm \ + < romm-db-pre-5.0.sql +``` + +### 4. Restore assets + config + +```sh +rsync -a /backup/romm/assets-pre-5.0/ /path/to/assets/ +rsync -a /backup/romm/config-pre-5.0/ /path/to/config/ +``` + +### 5. Start the old version + +```sh +docker compose up -d +``` + +Open a bug report on [GitHub](https://github.com/rommapp/romm/issues) with: + +- Your previous version. +- Whatever you saw in `docker logs romm` during the 5.0 startup. +- Your deployment (Docker Compose, Unraid, K8s, etc.). + +## Common upgrade issues + +### Migrations hang + +Watch `docker logs -f romm`. If Alembic is still running, it's still running — large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: + +- DB connection issues (`docker logs romm-db`). +- Out-of-memory kills (`dmesg | grep -i oom`). +- Missing new env vars that migrations rely on. + +### WebSockets fail after upgrade + +If you're behind a reverse proxy, the upgrade shouldn't change anything — but it's a good moment to verify the proxy forwards `Upgrade: websocket` properly. See [Reverse Proxy](../install/reverse-proxy.md). + +### OIDC users lose role, come back as Viewer + +5.0 introduces `OIDC_CLAIM_ROLES`. Before 5.0, OIDC users were always Viewer. After 5.0, if you set `OIDC_CLAIM_ROLES`, roles are reassigned on **every login** based on IdP claims. If your IdP doesn't emit the expected claim yet, every OIDC user gets demoted to Viewer. + +Fix: either don't set `OIDC_CLAIM_ROLES` (keeps 4.x behaviour), or wire the claim on the IdP side before enabling the mapping. + +See [OIDC Setup → Role mapping](../administration/oidc/index.md#role-mapping-50). + +### Scheduled tasks stop firing + +5.0 renamed several cron env vars. Check [Environment Variables](../reference/environment-variables.md) and update your env to the new names — otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). + +## After the upgrade + +Once everything's stable: + +- Pin your image to `rommapp/romm:5.0.0` in version control. +- Update your uptime monitor's heartbeat endpoint — still `/api/heartbeat`, but now exposes more info (see [Observability](../administration/observability.md)). +- Take a fresh backup of the post-migration state. You want a known-good 5.0 snapshot, not just the pre-5.0 one. +- Explore the new stuff in [What's New in 5.0](../getting-started/what-is-new-in-5.md). From 591c74f575e1a716abc266582b52b267fb293383 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 17:49:47 +0000 Subject: [PATCH 011/121] docs: draft reference/configuration-file.md (full config.yml schema) Full lookup-oriented reference for every section of config.yml: - exclude.* (platforms, single/multi-file roms, parts) - system.platforms + system.versions bindings - filesystem (roms_folder, firmware_folder [5.0], skip_hash_calculation) - scan.priority.metadata/artwork/region/language - scan.media (full media-type table) - scan.gamelist.export + scan.pegasus.export [5.0] - emulatorjs.debug / cache_limit / disable_batch_bootup / disable_auto_unload / netplay + ice_servers / settings / controls Provider-slug table for scan.priority.metadata, including TGDB + Libretro that arrived in 5.0. Links to batocera-retrobat and ES-DE example configs. Cross-links to folder-structure, metadata-providers, scanning-and-watcher, and environment-variables for related context. Builds clean with --strict. --- docs/reference/configuration-file.md | 415 ++++++++++++++++++++++++++- 1 file changed, 410 insertions(+), 5 deletions(-) diff --git a/docs/reference/configuration-file.md b/docs/reference/configuration-file.md index 1cfb8ff0..b3bd2206 100644 --- a/docs/reference/configuration-file.md +++ b/docs/reference/configuration-file.md @@ -1,10 +1,415 @@ --- -status: placeholder -wave: 1 +title: Configuration File +description: Full schema reference for config.yml — exclusions, system bindings, filesystem, scan priorities, EmulatorJS. --- # Configuration File -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM reads `config.yml` from `/romm/config/config.yml` inside the container. The whole file is optional — any section you omit falls back to RomM's defaults. + +You can edit `config.yml` directly on disk **or** through **Administration → Library Management** in the UI, which is a two-way view of the same file. + +Start from the [`config.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.example.yml) upstream. Two larger fully-worked examples for frontend-integration scenarios: + +- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml) +- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) + +!!! warning "Only set what you need" + Any omitted section uses the default. Don't copy the full example and then strip sections — just add what you want to change. + +--- + +## `exclude` + +Control what the scanner ignores. + +### `exclude.platforms` + +Skip entire platform folders. Values are platform slugs (not folder names — see [`system.platforms`](#systemplatforms) if your folders are named differently). + +```yaml +exclude: + platforms: ["ps", "ngc", "gba"] +``` + +### `exclude.roms.single_file.extensions` + +Drop files with these extensions before matching. Applies to files that aren't inside a multi-file folder. + +**Default:** `["db", "ini", "tmp", "bak", "lock", "log", "cache", "crdownload"]` + +```yaml +exclude: + roms: + single_file: + extensions: ["xml", "txt"] +``` + +### `exclude.roms.single_file.names` + +Unix-glob file-name patterns to skip. + +**Default:** `[".DS_Store", ".localized", ".Trashes", ".stfolder", "@SynoResource", "gamelist.xml"]` + +```yaml +exclude: + roms: + single_file: + names: ["info.txt", "._*", "*.nfo"] +``` + +### `exclude.roms.multi_file.names` + +Skip whole folders. Used for multi-disc / multi-file games you want invisible. + +**Default:** `["@eaDir", "__MACOSX", "$RECYCLE.BIN", ".Trash-*", ".stfolder", ".Spotlight-V100", ".fseventsd", ".DocumentRevisions-V100", "System Volume Information"]` + +```yaml +exclude: + roms: + multi_file: + names: ["final fantasy VII", "DLC"] +``` + +### `exclude.roms.multi_file.parts.names` + +Files **inside** a multi-file ROM folder to ignore. Useful for excluding `.nfo`, `._*` macOS attributes, etc. from multi-disc sets. + +**Default:** `[".DS_Store", ".localized", ".Trashes", ".stfolder", "@SynoResource", "gamelist.xml"]` + +```yaml +exclude: + roms: + multi_file: + parts: + names: ["data.xml", "._*"] +``` + +### `exclude.roms.multi_file.parts.extensions` + +Extensions to ignore inside a multi-file ROM folder. + +**Default:** `["db", "ini", "tmp", "bak", "lock", "log", "cache", "crdownload"]` + +```yaml +exclude: + roms: + multi_file: + parts: + extensions: ["xml", "txt"] +``` + +--- + +## `system` + +Customise how RomM interprets your filesystem layout. + +### `system.platforms` + +Map your folder names to RomM platform slugs. Left side = your folder name, right side = canonical slug (see [Supported Platforms](../platforms/supported-platforms.md) for the full list). + +```yaml +system: + platforms: + gc: "ngc" # treat "gc/" folder as GameCube + psx: "ps" # treat "psx/" folder as PlayStation + super_nintendo: "snes" +``` + +### `system.versions` + +Associate a platform with its "main" IGDB version. Useful for platforms that have multiple IGDB entries you want collapsed into one (e.g. NAOMI → Arcade). + +```yaml +system: + versions: + naomi: "arcade" +``` + +--- + +## `filesystem` + +### `filesystem.roms_folder` + +Override the default ROMs folder name (`roms`). + +```yaml +filesystem: + roms_folder: "my_roms" +``` + +### `filesystem.firmware_folder` _(new in 5.0)_ + +Override the default BIOS/firmware folder name (`bios`). + +```yaml +filesystem: + firmware_folder: "firmware" +``` + +### `filesystem.skip_hash_calculation` + +Skip hashing on low-power devices. You lose hash-based matching (RetroAchievements, Hasheous, PlayMatch) but scans run much faster. + +**Default:** `false` + +```yaml +filesystem: + skip_hash_calculation: true +``` + +--- + +## `scan` + +### `scan.priority.metadata` + +Order metadata providers are queried during a scan. First match wins for descriptive fields (title, description, release date, etc.). + +**Default:** `["igdb", "moby", "ss", "ra", "launchbox", "gamelist", "hasheous", "flashpoint", "hltb"]` + +```yaml +scan: + priority: + metadata: + - "igdb" + - "ss" + - "moby" +``` + +Values are the provider slugs. Full list: + +| Slug | Provider | +| --- | --- | +| `igdb` | IGDB | +| `moby` | MobyGames | +| `ss` | ScreenScraper | +| `ra` | RetroAchievements | +| `launchbox` | LaunchBox | +| `gamelist` | gamelist.xml importer | +| `hasheous` | Hasheous | +| `flashpoint` | Flashpoint | +| `hltb` | HowLongToBeat | +| `tgdb` | TheGamesDB _(5.0)_ | +| `libretro` | Libretro metadata _(5.0)_ | + +See [Metadata Providers](../administration/metadata-providers.md) for context on each. + +### `scan.priority.artwork` + +Same idea, for cover art and screenshots. Defaults to the same order as `scan.priority.metadata` but can differ. + +```yaml +scan: + priority: + artwork: + - "ss" # prefer ScreenScraper artwork + - "igdb" + - "moby" +``` + +### `scan.priority.region` _(enhanced in 5.0)_ + +Preferred region for titles, cover art, and regional variants. ScreenScraper uses this directly; other providers respect it where possible. + +**Default:** `["us", "wor", "ss", "eu", "jp"]` + +```yaml +scan: + priority: + region: + - "us" + - "eu" + - "jp" +``` + +### `scan.priority.language` _(enhanced in 5.0)_ + +Preferred localisation language. + +**Default:** `["en", "fr"]` + +```yaml +scan: + priority: + language: + - "en" + - "es" + - "fr" +``` + +### `scan.media` + +Which media types to fetch during a scan. Applies primarily to ScreenScraper and the gamelist.xml importer. + +| Type | Description | +| --- | --- | +| `box2d` | Normal 2D cover art. Always enabled. | +| `box3d` | 3D box art. | +| `miximage` | Composite image (box + screenshot + logo). | +| `physical` | Physical media (disc, cartridge). | +| `screenshot` | In-game screenshot. Enabled by default. | +| `title_screen` | Title-screen capture. | +| `marquee` | Transparent logo. | +| `fanart` | Community-uploaded fan art. | +| `bezel` | EmulatorJS-compatible bezel. | +| `manual` | PDF manual. Enabled by default. | +| `video` | Gameplay video (big files — watch your disk). | + +```yaml +scan: + media: + - box2d + - screenshot + - manual + - bezel +``` + +### `scan.gamelist.export` _(new in 5.0)_ + +Generate a `gamelist.xml` in each platform folder, compatible with ES-DE / Batocera. + +```yaml +scan: + gamelist: + export: true + media: + thumbnail: box2d + image: screenshot +``` + +### `scan.pegasus.export` _(new in 5.0)_ + +Export metadata in Pegasus-frontend format (`metadata.pegasus.txt`). + +```yaml +scan: + pegasus: + export: true +``` + +--- + +## `emulatorjs` + +Configure the in-browser EmulatorJS player. + +### `emulatorjs.debug` + +Log available EmulatorJS options to the browser console for debugging. + +**Default:** `false` + +```yaml +emulatorjs: + debug: true +``` + +### `emulatorjs.cache_limit` + +Per-ROM cache limit in bytes. `null` = unlimited. + +```yaml +emulatorjs: + cache_limit: 52428800 # 50 MB +``` + +### `emulatorjs.disable_batch_bootup` + +DOS-specific. Skips the `autorun.bat` step. Try toggling if DOS games won't boot. + +```yaml +emulatorjs: + disable_batch_bootup: true +``` + +### `emulatorjs.disable_auto_unload` + +By default, EmulatorJS stops the emulator when you leave its page. Disable to keep it running across navigation. + +```yaml +emulatorjs: + disable_auto_unload: true +``` + +### `emulatorjs.netplay` _(new in 5.0)_ + +Toggle Netplay and configure STUN/TURN servers. Google's public STUN servers are fine for most setups; run your own [coturn](https://github.com/coturn/coturn) or use [Metered's free tier](https://www.metered.ca/stun-turn) if you need TURN (symmetric NAT). + +```yaml +emulatorjs: + netplay: + enabled: true + ice_servers: + - urls: "stun:stun.l.google.com:19302" + - urls: "stun:stun1.l.google.com:19302" + - urls: "stun:stun2.l.google.com:19302" + - urls: "turn:openrelay.metered.ca:80" + username: "openrelayproject" + credential: "openrelayproject" + - urls: "turn:openrelay.metered.ca:443" + username: "openrelayproject" + credential: "openrelayproject" +``` + +!!! note "Nightly CDN caveat" + With Netplay enabled, EmulatorJS loads some assets (localisations included) from its nightly CDN (`https://cdn.emulatorjs.org/nightly/...`). Occasional 404s or untranslated strings can appear when the nightly has a transient mismatch. Usually self-heals by the next RomM image update. + +### `emulatorjs.settings` + +Per-core emulator options. Use `default` to apply to every core. + +```yaml +emulatorjs: + settings: + parallel_n64: + vsync: disable + snes9x: + snes9x_region: ntsc + default: + fps: show +``` + +Core names must match the EmulatorJS core identifier exactly — see the `getSupportedEJSCores` utility in the frontend source for the full list, or leave the core out and use `default`. + +### `emulatorjs.controls` + +Map keyboard and controller buttons per core, per player. + +```yaml +emulatorjs: + controls: + snes9x: + 0: # player 1 + 0: # button slot + value: x # keyboard key + value2: BUTTON_2 # controller button + 1: # player 2 + 0: + value: / + value2: BUTTON_2 +``` + +See the [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/) for the button-slot reference. + +--- + +## Editing via the UI + +Everything above is also available from the Library Management page in the web UI — edits there write back to the same `config.yml`. Either path works; they're not separate stores. + +## Per-file alternatives + +RomM also ships two pre-built config.yml variants for people coming from existing frontends — copy them wholesale rather than writing one from scratch: + +- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml) — Batocera / RetroBat layouts. +- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) — ES-DE layout. + +## Related + +- [Folder Structure](../getting-started/folder-structure.md) — how the filesystem shape interacts with `config.yml`. +- [Metadata Providers](../administration/metadata-providers.md) — per-provider detail for the `scan.priority.*` slugs. +- [Scanning & Watcher](../administration/scanning-and-watcher.md) — how `exclude.*` interacts with scan runs. +- [Environment Variables](environment-variables.md) — env-var overrides for some of the same knobs. From 3b399ab586cf086748800d7c5e58db75c2be433e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 17:54:54 +0000 Subject: [PATCH 012/121] docs: start Wave 2 end-user section (6 pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opens the Wave 2 end-user bucket — headline collection + TV/gamepad features that were completely undocumented before. - using/index.md: hub with browse/organise/personal/special/tools groupings, cross-linking to every sub-page - using/library.md: ported and expanded from Usage/LibraryManagement. Dashboard ribbons, menu bar, grid/list toggle, full game-card context menu, toggle + value filters, search, platform view with firmware button, game view tab structure (Details / Game Data / Personal / Manual / Time to Beat / Screenshots / Achievements / Related Games / Additional Content) - using/collections.md: NEW hand-curated collections — create from scratch or from a search, add/remove ROMs, visibility, sharing, delete semantics, role matrix, API surface - using/smart-collections.md: NEW 5.0 feature. Rule structure (field/operator/value, all/any), supported fields (metadata + personal data + flags), operator matrix, four worked examples, public/private nuance (personal fields stay "yours"), refresh behaviour, limitations, API - using/virtual-collections.md: NEW 5.0 feature. Dimensions table (genre/developer/publisher/franchise/year/decade/age/region/tags), toggle-a-dimension on/off, what you can't do (read-only), "why isn't my game in X" metadata troubleshooting - using/console-mode.md: NEW 5.0 feature. How to get to /console, gamepad + keyboard + touch navigation, default bindings table, differences from main UI (spatial nav, SFX, sizing, auto-hide cursor), feature parity matrix, controller/audio/grid settings, pre-launch disc/save/state picker, handheld pointers to Grout and Argosy, troubleshooting Builds clean with --strict. --- docs/using/collections.md | 105 ++++++++++++++++++- docs/using/console-mode.md | 162 +++++++++++++++++++++++++++++- docs/using/index.md | 48 ++++++++- docs/using/library.md | 159 ++++++++++++++++++++++++++++- docs/using/smart-collections.md | 154 +++++++++++++++++++++++++++- docs/using/virtual-collections.md | 96 +++++++++++++++++- 6 files changed, 694 insertions(+), 30 deletions(-) diff --git a/docs/using/collections.md b/docs/using/collections.md index b419c1bd..80929685 100644 --- a/docs/using/collections.md +++ b/docs/using/collections.md @@ -1,10 +1,105 @@ --- -status: placeholder -wave: 2 +title: Collections +description: Hand-curated ROM collections — create, edit, share, and delete. --- # Collections -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +A **collection** is a named group of ROMs you curate by hand. Add or remove games one at a time; the list is exactly what you pick. + +RomM has three collection types. This page covers **standard** collections. For the other two: + +- [Smart Collections](smart-collections.md) — rule-based, auto-populating. +- [Virtual Collections](virtual-collections.md) — auto-generated by RomM (by genre, developer, year, tags). + +## Creating a collection + +Two ways: + +### From the Collections drawer + +1. Click **Collections** in the menu bar → **+ New Collection**. +2. Pick a name, optional description, optional cover image. +3. Set **visibility**: + - **Private** — only you see it. + - **Public** — any signed-in user on this instance sees it. Needs `collections.write` scope to toggle (Editor or Admin). +4. Save. The collection is created empty. + +### From a search result + +1. Run a search or apply filters in the library. +2. Click the **New Collection from results** icon next to the search bar. +3. Name it and save — RomM populates it with the current result set. + +This is the fastest way to bootstrap a collection from "all the PSX games tagged (USA)" or similar. + +## Adding ROMs + +Hover a game card → context menu (…) → **Add to Collection** → pick the collection. Repeat for each ROM. + +Or from the game detail page → Context menu → **Collections** → toggle. + +Bulk add is not available in 5.0 for standard collections — use [Smart Collections](smart-collections.md) if you want rule-based population. + +## Removing ROMs + +Same paths in reverse. Context menu → **Remove from Collection**. + +From the collection view, you can also bulk-select ROMs and remove them via the multi-select toolbar. + +## Editing a collection + +Open the collection → click the collection drawer icon → **Edit**. + +Change: + +- Name. +- Description. +- Cover image. +- Visibility (Private / Public). + +## Sharing + +If the collection is **Public**, any other user on the same RomM instance sees it in their Collections drawer. They can browse, play, download — but can't add or remove ROMs. Only the owner edits. + +For cross-instance sharing, generate a link via the collection drawer → **Copy link** — anyone with access to your RomM instance and the link can open it directly. + +## Deleting + +Collection drawer → **Delete** → confirm. + +Deletion only removes the collection itself. The ROMs inside it stay in the library — nothing touches the actual files. + +## Who can do what + +| Action | Viewer | Editor | Admin | +| --- | :---: | :---: | :---: | +| See public collections | ✓ | ✓ | ✓ | +| Create own private collection | ✓ | ✓ | ✓ | +| Edit own collection | ✓ | ✓ | ✓ | +| Make a collection public | — | ✓ | ✓ | +| See all users' collections (admin panel) | — | — | ✓ | + +Scope mapping and the full permission matrix in [Users & Roles](../administration/users-and-roles.md#scope-matrix). + +## Tips + +- **Don't make a collection per genre** — you want [Virtual Collections](virtual-collections.md) for that, auto-populated. +- **Don't make a collection for "Backlogged games"** — that's what the Personal → Status flag is for, and it's filterable without a collection. +- **Do make a collection for curated playthroughs** — "RomM Staff Picks", "Games I Want to Stream", "Favourites for My Kid", "Halloween Games". Hand-picked context is what collections are for. + +## API + +The standard-collection REST surface is under `/api/collections/`: + +```http +GET /api/collections # list +POST /api/collections # create +GET /api/collections/{id} # get +PUT /api/collections/{id} # update +DELETE /api/collections/{id} # delete +POST /api/collections/{id}/roms # add ROMs (body: array of ROM IDs) +DELETE /api/collections/{id}/roms # remove ROMs +``` + +Full details in the [API Reference](../developers/api-reference.md). Requires `collections.read` / `collections.write` scopes. diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md index ad6bd22a..ac3ed13b 100644 --- a/docs/using/console-mode.md +++ b/docs/using/console-mode.md @@ -1,10 +1,162 @@ --- -status: placeholder -wave: 2 +title: Console Mode +description: TV + gamepad UI for RomM. Spatial navigation, SFX, focus sounds, no mouse needed. --- # Console Mode -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +**Console Mode** is a second UI for RomM, living at `/console`, designed for TV screens and gamepad input. Same instance, same data, completely different UX. + +New in 5.0. If you're on a desktop with a keyboard and mouse, you probably don't need it — the main [Library](library.md) view is your home. But if you're on: + +- A TV with RomM on a stick / Shield / Pi / mini-PC, +- A handheld running muOS / Knulli / Batocera / Steam Deck, +- A browser tab full-screened with a gamepad plugged in, + +…you want Console Mode. + +## Getting there + +Three ways: + +1. **Menu bar → Console icon** (looks like a small controller). +2. Type `/console` after your RomM URL: `https://romm.example.com/console`. +3. Set the default view to Console Mode in **Profile → User Interface → Default view** — signs you in straight into `/console` every time. + +Bookmarking `/console` on your TV browser is the most common pattern. + +## Navigating + +### Gamepad + +Standard gamepad API. Xbox, PS, 8BitDo, Steam Controller, Switch Pro — anything the browser sees as a standard gamepad works. Handhelds with native gamepad input (Steam Deck, muOS devices) work out of the box. + +Default bindings (most platforms, configurable): + +| Button | Action | +| --- | --- | +| D-pad / left stick | Move focus | +| A (bottom) | Activate | +| B (right) | Back / cancel | +| X (top) | Play | +| Y (left) | Details / context menu | +| LB / RB | Switch tabs or scroll by page | +| LT / RT | Jump to start / end of grid | +| Start | Main menu | +| Select / Back | Filters | + +Button labels follow the Xbox convention regardless of actual controller — "A" is always the bottom face button. + +### Keyboard (fallback) + +If you're browsing `/console` without a gamepad for some reason: + +| Key | Action | +| --- | --- | +| Arrow keys | Move focus | +| Enter / Space | Activate | +| Escape | Back | +| `/` | Search | +| `f` | Filters | + +### Touch + +Console Mode is gamepad-first, but touch works on handhelds without native gamepad input — tap to activate, swipe to scroll. + +## What's different from the main UI + +### Spatial navigation + +Every focusable element is placed on a 2D grid. D-pad Up / Down / Left / Right moves to the visually-nearest focusable element — not the next DOM element. Works the way a TV UI should. + +### Focus sounds + +Subtle SFX play on focus, activate, and back. Turn off in **Settings → Console Mode → Audio**. + +### Bigger hit targets + +Cards and buttons are sized for 10-foot viewing. Grids show fewer items per row than the main UI. + +### Simpler information density + +The game detail view uses full-screen tabs instead of side-by-side panels. Filter panels are dedicated screens rather than popovers. + +### Mouse-cursor auto-hide + +The cursor disappears after a few seconds of no movement. Wiggle the mouse (or touch the screen) to bring it back. + +### Theme + +Console Mode uses its own color palette — higher contrast, bigger typography. It follows the main UI's dark/light mode setting but with TV-appropriate rendering. + +## Features + +Most of the main UI's features are available in Console Mode: + +- ✓ Browse library, platforms, collections. +- ✓ Search and filters. +- ✓ Play (launches [In-Browser Play](in-browser-play.md) or shows a download prompt if the platform isn't browser-playable). +- ✓ Saves and states — upload, select, delete. +- ✓ Smart and virtual collections. +- ✓ RetroAchievements progression display. + +Not yet in Console Mode (still use the main UI): + +- ROM editing (match, edit metadata). +- Scanning. +- Administration (users, tasks, stats). +- ROM Patcher. + +## Configuring + +### Controller mapping + +**Settings → Console Mode → Controls.** Rebind any action to any button. Per-user preferences. + +### Audio + +**Settings → Console Mode → Audio.** Toggle SFX, adjust volume. + +### Grid layout + +**Settings → Console Mode → Grid size.** Pick small / medium / large card sizes. Large is best for 10-foot viewing; small is for close viewing on a phone or handheld. + +### Background art + +**Settings → Console Mode → Backgrounds.** Use the focused game's screenshot as a faded background — makes Console Mode feel like a proper console UI. Off by default. + +## Launching games + +Focus a game → press **A** (Play). + +If the platform supports [In-Browser Play](in-browser-play.md), the emulator loads full-screen. Controllers and keyboard pass through automatically. + +If not, Console Mode shows a **Download** prompt with a QR code for mobile sharing. + +### Pre-launch disc/save/state picker + +For multi-disc games, Console Mode asks which disc to boot before launching. For games with existing saves/states, you can pick which to resume from. No dialogs in the middle of a session — everything's picked up front. + +## Known limitations + +- **Admin features aren't available** — if you're the admin, flip back to the main UI for scans / user management. +- **Some metadata tabs collapse** — the main UI's "Related Games" + "Additional Content" tabs may be merged on the smaller Console detail view. +- **Mobile browsers with no gamepad** — touch works, but the UX is designed for gamepads, not fingers. Use the main UI or the Argosy mobile app ([Ecosystem](../ecosystem/argosy.md)). + +## Handheld-specific notes + +Running on muOS / Batocera / Knulli / a Steam Deck? Consider: + +- **[Grout](../ecosystem/grout.md)** — official handheld companion that syncs saves/states to/from the device. +- **[Argosy Launcher](../ecosystem/argosy.md)** — Android handhelds that can run a browser, but want a native-feeling app. + +Both use [Client API Tokens](../ecosystem/client-api-tokens.md) for auth. + +## Troubleshooting + +- **Gamepad not detected** — Chrome sometimes needs a button press on the page before enumerating gamepads. Press any button and it'll show up. +- **Cursor stays visible** — you have a USB mouse plugged into a handheld. Unplug it or set **Cursor hide** to "always" in Console settings. +- **Laggy navigation** — low-powered device running a heavy browser. Try Firefox or a lighter browser build. +- **SFX plays during video** — turn it off in **Console → Audio** or lower volume. + +More in [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/using/index.md b/docs/using/index.md index 2489e7da..dd632c46 100644 --- a/docs/using/index.md +++ b/docs/using/index.md @@ -1,10 +1,48 @@ --- -status: placeholder -wave: 2 +title: Using RomM +description: End-user guide — how to browse, play, collect, patch, and share from the RomM UI. --- # Using RomM -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +You've got an account, you're signed in. This section covers everything you do as a user — not an admin. + +Admins: see [Administration](../administration/index.md) for operator tasks. + +## Browse and play + +- **[Library](library.md)** — the main UI: home dashboard, platform and game cards, filters, the search bar. +- **[In-Browser Play](in-browser-play.md)** — EmulatorJS for retro consoles, Ruffle for Flash. Save states, cheats, fullscreen. +- **[Downloads](downloads.md)** — single, bulk, QR code, copy link. +- **[Uploads](uploads.md)** — drag-and-drop ROMs, firmware, saves, states, screenshots. + +## Organise + +- **[Collections](collections.md)** — hand-curated lists. +- **[Smart Collections](smart-collections.md)** — rule-based, auto-populating. +- **[Virtual Collections](virtual-collections.md)** — auto-generated by genre, developer, year, tags. + +## Personal data + +- **[Saves & States](saves-and-states.md)** — upload, sync, select before launching. +- **[RetroAchievements](retroachievements.md)** — link your RA account, surface progression. +- **[Account & Profile](account-and-profile.md)** — change your password, email, avatar. API tokens. + +## Special modes + +- **[Console Mode](console-mode.md)** — `/console` UI for TVs and gamepads. Spatial navigation, SFX, no mouse needed. +- **[Install as PWA](pwa.md)** — add RomM to your phone/desktop home screen. +- **[Mobile & TV](mobile-and-tv.md)** — device-specific usage patterns. + +## Tools + +- **[ROM Patcher](rom-patcher.md)** — apply IPS/UPS/BPS/PPF and more in-browser. +- **[Netplay](netplay.md)** — EmulatorJS multiplayer sessions. + +## Settings + +- **[Languages](languages.md)** — 19 locales. + +--- + +Looking for something specific? Every page in this section is linked from the dashboard or the profile drawer. If you can't find a feature, check [Core Concepts](../getting-started/concepts.md) — the vocabulary index. diff --git a/docs/using/library.md b/docs/using/library.md index f6d73ad4..c1909bf8 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -1,10 +1,159 @@ --- -status: placeholder -wave: 2 +title: Library +description: Browse your RomM library — dashboard, cards, filters, search, and the platform/game views. --- + + # Library -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +The library is the heart of RomM. This page covers the day-to-day UI: the dashboard, game and platform cards, filters, search, and the detail views. + +## Dashboard + +Home screen. Several **ribbons** of content: + +- **Recently Added** — a carousel of the latest ROMs RomM has indexed. +- **Continue Playing** — games with an active play session. See [Play Sessions](../getting-started/concepts.md#play-session). +- **Platforms** — every platform that has at least one ROM. Cards link into the [platform view](#platform-view). +- **Collections** — your [collections](collections.md), [smart collections](smart-collections.md), and [virtual collections](virtual-collections.md). + +Each ribbon can be hidden or shown from **Profile → User Interface** — handy if you prefer a tight dashboard. + +## Menu bar + +Visible everywhere. Shortcuts to: + +- **Search** — global search across every ROM, metadata field, and tag. +- **Platforms** — drawer listing every platform as a link. +- **Collections** — drawer listing every collection type. +- **Scan** — launches a scan. Permission-gated (Admin/Editor). +- **Console Mode** — jumps to the `/console` TV/gamepad UI. See [Console Mode](console-mode.md). +- **Upload** — permission-gated (Admin/Editor). See [Uploads](uploads.md). +- **Profile** — profile drawer + admin panel. + +## Grid/list toggle + +Every gallery (platform view, collection view, search results) has a grid-vs-list toggle in the top right. Grid is the default — card thumbnails at scale. List is denser — one row per ROM, sortable columns for title, release date, rating, playtime, region. + +## Game cards + +![game card](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/gameCard.png) + +Hovering over a game card exposes: + +- **Play** — launch in [EmulatorJS or Ruffle](in-browser-play.md), if the platform supports in-browser play. +- **Download** — single-file download. See [Downloads](downloads.md) for bulk, QR, and copy-link options. +- **Context menu (…)** — opens the card's action menu. + +Card context menu: + +![context menu](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/ContextMenu.png) + +- **Match to metadata agent** — manually rematch against a provider (IGDB, ScreenScraper, etc.). +- **Edit** — change title, platform, or other metadata fields (permission-gated). +- **Refresh metadata** — re-query providers and overwrite. +- **Add/remove from Favourites**. +- **Add/remove from Collection** — surfaces your existing collections. + +Double-click (or tap) the card to open the [game view](#game-view). + +## Filters + +Every gallery has a Filters button in the top right. Filter combinations stack — RomM shows only games matching all active filters. + +### Toggle filters + +- **Show Unmatched** — games with no provider match. +- **Show Matched** — the inverse. +- **Show Favourites** — your personal favourites. +- **Show Duplicates** — games appearing more than once. +- **Show Playables** — only games with in-browser play support on this platform. +- **Show Missing** — DB entries whose files are gone. +- **Show Verified** — matched via Hasheous. +- **Show RetroAchievements** — games RetroAchievements has achievements for. See [RetroAchievements](retroachievements.md). + +### Value filters + +Dropdowns that cross-reference metadata on the visible set: + +- **Platform** — scope to one platform. +- **Genre / Franchise / Collections / Company / Age Rating / Region / Language** — metadata dimensions. +- **Status** — personal play status (Never Played, Backlogged, Playing, Complete, Hidden). + +Filters narrow down *what you're currently viewing*. Search first → filter second, and the filter dropdowns only show values present in the search results. + +## Search + +Click the search icon in the menu bar. Real-time search against: + +- Game title. +- Tags (regions, languages, revisions, arbitrary `[]`/`()` tags). +- Metadata fields (genre, developer, publisher, summary). + +Search is platform-aware — type `zelda` and you'll see every matched Zelda across every platform you have. + +![search bar](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/SearchBar.png) + +Two icons next to the search bar: + +- **View filters** — same filter panel as above. +- **New collection from results** — saves the current search + filter state as a [standard collection](collections.md). + +## Platform view + +Clicking a platform card takes you to the platform view: every game on that platform, browseable with the same filters and sorts. + +![platform drawer](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/PlatformView.png) + +Two side buttons: + +- **Platform drawer** — metadata for the platform itself: name, slug, category, generation, IGDB version, active providers, cover-art style setting. Admins see an **Upload** + **Scan** shortcut + a Danger Zone with "Delete Platform" (removes the DB entry; files on disk are not touched — a rescan re-creates the platform). +- **Firmware** — every firmware file registered against the platform, plus an upload button. See [Firmware Management](../administration/firmware-management.md). + +## Collection view + +Like the platform view, but scoped to one collection. Same filters and grid/list toggle. The side drawer shows collection metadata (name, owner, visibility, game count) and, for collections you own, Edit and Delete buttons. + +Three collection types — see [Collections](collections.md), [Smart Collections](smart-collections.md), [Virtual Collections](virtual-collections.md). + +## Game view + +Click (or tap) a game. The game view has two halves: + +### Left — hero panel + +- Cover art (or `vanilla-tilt` 3D hover if enabled in UI settings). +- **Play** button (if playable). +- **Download** button + menu (Copy Link, QR Code, Bulk Download). +- **Upload ROM** — replace / add a new version. +- Context menu — Match, Edit, Match rehash, Delete, etc. (permission-gated). + +### Right — tabs + +Which tabs appear depends on your metadata providers: + +- **Details** — title, description, release date, genres, developer/publisher, regions, rating, matched providers. Filterable metadata surfaces here. +- **Game Data** — save files, save states, screenshots. Per-user. Upload, download, and delete. See [Saves & States](saves-and-states.md). +- **Personal** — your data on this game: status (Never Played / Backlogged / Playing / Complete / Hidden), rating, difficulty, percent complete, notes. Stored per-user. +- **Manual** — PDF viewer if you have a manual for this game. +- **Time to Beat** — [HowLongToBeat](../administration/metadata-providers.md#howlongtobeat) data if enabled. +- **Screenshots** — provider-fetched + user-uploaded screenshots. +- **Achievements** — [RetroAchievements](retroachievements.md) progression if you've linked your RA account. +- **Related Games** — similar titles from IGDB. +- **Additional Content** — DLCs, patches, hacks, mods, translations found inside the multi-file folder. + +## Keyboard shortcuts + +| Key | Action | +| --- | --- | +| `/` | Focus search. | +| `g h` | Go home. | +| `g s` | Open Search page. | +| `Esc` | Close open drawer or dialog. | + +Full shortcut list is on the [Account & Profile](account-and-profile.md) page. + +## Console mode alternative + +If you're on a TV / gamepad, there's a second UI designed for it: **Console Mode**. Same library, different navigation. See [Console Mode](console-mode.md). diff --git a/docs/using/smart-collections.md b/docs/using/smart-collections.md index 19dec0cf..4d81a340 100644 --- a/docs/using/smart-collections.md +++ b/docs/using/smart-collections.md @@ -1,10 +1,154 @@ --- -status: placeholder -wave: 2 +title: Smart Collections +description: Rule-based collections that auto-populate from your library — new in 5.0. --- # Smart Collections -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +A **smart collection** is a collection defined by **rules**, not by hand-picking. You describe what's in it ("all SNES games rated ≥4 stars", "everything in the Zelda franchise", "games I've beaten"), and RomM keeps the list in sync automatically — as you add ROMs, update ratings, mark games complete, the collection updates. + +New in 5.0. For hand-curated collections, see [Collections](collections.md). For auto-generated-by-RomM groupings, see [Virtual Collections](virtual-collections.md). + +## Creating a smart collection + +1. **Collections** drawer → **+ New Smart Collection**. +2. Name it. +3. Build the rule set — add one or more **conditions**. +4. Save. + +The collection is live immediately — populated with every ROM currently matching the rules, and updated on every relevant change afterward. + +## Rule structure + +A smart collection is one or more **conditions** joined by **all** (AND) or **any** (OR). + +Each condition has three parts: + +1. **Field** — what you're matching on. +2. **Operator** — comparison (equals, contains, greater-than, etc.). +3. **Value** — the thing you're comparing against. + +## Supported fields + +### Metadata + +- **Platform** — platform slug. +- **Title** — game title (case-insensitive substring match with `contains`). +- **Genre** — IGDB genre tag. +- **Franchise** — game franchise (Mario, Final Fantasy, etc.). +- **Developer** — company that made the game. +- **Publisher** — company that released it. +- **Release Year** — year. +- **Age Rating** — ESRB / PEGI rating. +- **Region** — game region (USA, Japan, Europe, World, etc.). +- **Language** — game language. +- **Rating** — IGDB / ScreenScraper critic score. + +### Personal data + +- **Personal Rating** — your per-game rating. +- **Status** — Never Played / Backlogged / Playing / Complete / Hidden. +- **Playtime** — accumulated play time (minutes). +- **Favourites** — whether you've favourited it. +- **Has Achievements** — whether the game has [RetroAchievements](retroachievements.md). + +### Flags + +- **Matched** — has a provider ID. +- **Playable in browser** — supports EmulatorJS / Ruffle. +- **Has Firmware** — required firmware exists in the library. +- **Duplicate** — the same game appears twice. + +## Operators + +Available operators depend on the field type: + +| Operator | Works with | +| --- | --- | +| `equals`, `not equals` | Everything. | +| `contains`, `does not contain` | Text fields. | +| `starts with`, `ends with` | Text fields. | +| `is`, `is not` | Boolean / enum fields (Status, Matched, Favourites). | +| `greater than`, `less than`, `between` | Numeric fields (Rating, Playtime, Release Year). | +| `in`, `not in` | Multi-value fields (Region, Language, Genre). | + +## Examples + +### "SNES RPGs I haven't finished" + +```text +all of: + - Platform equals "snes" + - Genre in ["RPG"] + - Status is not "Complete" +``` + +### "Top-rated arcade games that run in browser" + +```text +all of: + - Platform equals "arcade" + - Rating greater than 85 + - Playable in browser is "yes" +``` + +### "Everything I've touched recently but not finished" + +```text +all of: + - Playtime greater than 60 + - Status is not "Complete" +``` + +### "Zelda franchise, any platform" + +```text +any of: + - Franchise equals "The Legend of Zelda" + - Title contains "zelda" +``` + +(Combine `all of` + `any of` by nesting — the rule editor supports groups.) + +## Public / private + +Same visibility model as standard collections: + +- **Private** — only you see it (your personal data fields matter). +- **Public** — everyone on the instance sees it. *Your* personal-data rules still apply — if your smart collection is "games I haven't finished", every user sees *your* unfinished games. + +For shared rule sets across users, use metadata-only fields and keep the collection public. Rules that reference Personal data (status, rating, playtime, favourites) only make sense as private collections. + +## Refresh behaviour + +Smart collections refresh: + +- **Live** — when you add, remove, rate, or edit a ROM, RomM re-evaluates rules instantly. +- **On scan** — after every scan, rules are re-evaluated against the new state. +- **No manual refresh needed** — but admins can trigger a full re-evaluation via **Administration → Tasks → Refresh Smart Collections** if something looks stale. + +## Editing a rule + +Open the smart collection → drawer → **Edit**. The rule builder reopens with current rules pre-loaded. Save → collection updates immediately. + +## Deleting + +Same as standard collections — removes the definition. ROMs stay in the library. + +## Limitations + +- **No nested smart collections** — a smart collection can't reference another collection as a source. Compose rules directly. +- **Performance** — very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible; mentioned here for completeness. +- **Timezone** — "Release Year" uses UTC, not the user's timezone. Edge-case edge-of-year games might fall on the "wrong" side. + +## API + +```http +POST /api/collections/smart # create +GET /api/collections/smart # list +GET /api/collections/smart/{id} # get +PUT /api/collections/smart/{id} # update +DELETE /api/collections/smart/{id} # delete +``` + +Rule schema is part of the POST body — see the [API Reference](../developers/api-reference.md) for the JSON structure. Requires `collections.read` / `collections.write`. diff --git a/docs/using/virtual-collections.md b/docs/using/virtual-collections.md index 7a2332ae..6c9c26c7 100644 --- a/docs/using/virtual-collections.md +++ b/docs/using/virtual-collections.md @@ -1,10 +1,96 @@ --- -status: placeholder -wave: 2 +title: Virtual Collections +description: Auto-generated groupings by genre, developer, year, tag — no rules to write. --- # Virtual Collections -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +**Virtual collections** are auto-generated by RomM. You don't create or edit them — RomM looks at your library and groups ROMs by common metadata dimensions. + +New in 5.0. Compare with: + +- [Collections](collections.md) — you pick each ROM by hand. +- [Smart Collections](smart-collections.md) — you write rules, RomM populates. +- **Virtual Collections** — RomM picks both the groupings *and* the contents. + +## What gets grouped + +Virtual collections are generated for several dimensions: + +| Dimension | Example collections | +| --- | --- | +| **Genre** | "Platformer", "RPG", "Shooter", "Puzzle", "Fighting" | +| **Developer** | "Nintendo EAD", "Capcom", "Konami", "id Software" | +| **Publisher** | "Nintendo", "Sega", "Sony" | +| **Franchise** | "The Legend of Zelda", "Final Fantasy", "Mega Man" | +| **Release Year** | "1995", "2003", etc. | +| **Decade** | "1980s", "1990s", "2000s" | +| **Age Rating** | "ESRB: E", "PEGI: 7+" | +| **Region** | "USA", "Japan", "Europe" | +| **Tags** | Any `[]`/`()` tag from filenames — see [Folder Structure → Naming convention](../getting-started/folder-structure.md#naming-convention) | + +Collections with too few ROMs are suppressed — you won't see a "Developer: Some Indie Studio" with one entry. + +## Turning them on and off + +All dimensions on by default. To turn a dimension off: + +**Profile → User Interface → Virtual Collections → toggle a dimension.** + +Disabling a dimension hides all its collections from your drawer and dashboard. The underlying data is still there; you can re-enable any time. + +## Using them + +Virtual collections show up alongside regular and smart collections in: + +- The **Collections** drawer. +- The dashboard's **Collections** ribbon (if enabled). +- The filter panel under "Collection" — so you can filter any gallery by virtual collection membership. + +Open one like any collection — grid/list view, filters, play, download, etc. + +## What you *can't* do + +Virtual collections are read-only. You can't: + +- Add a ROM by hand (use a [standard collection](collections.md)). +- Remove a ROM (its metadata determines membership — fix the metadata if it's wrong). +- Make one "public" vs "private" — they're per-user, always visible to you. +- Rename them (they take their name from the metadata dimension). +- Set a custom cover image. + +If any of that matters, create a standard or smart collection instead. + +## Refresh + +Virtual collections recompute on every scan and on every metadata edit. No manual refresh needed. + +## "Why isn't my game in [X]?" + +Almost always metadata. A game missing from "Virtual Collection: RPG" probably doesn't have the `RPG` genre set on its IGDB entry. Options: + +1. **Manually edit the genre** on the game (permission-gated). +2. **Run an Unmatched scan** — sometimes IGDB has updated. +3. **Leave it** — the rest of the genre's collection still works. + +Genre coverage varies by metadata provider: + +- **IGDB** — rich genre data. +- **ScreenScraper** — ok genre data; some provider-specific labels. +- **Hasheous / PlayMatch** (hash-only) — no genre data; they proxy IGDB. + +If your library is heavily ScreenScraper-matched and you want rich genres, add IGDB and run an **Unmatched** or **Update Metadata** scan. See [Metadata Providers](../administration/metadata-providers.md). + +## API + +Virtual collections are read-only via API: + +```http +GET /api/collections/virtual # list all virtual collections +GET /api/collections/virtual?type=genre # filter by dimension +GET /api/collections/virtual/{id} # get one +``` + +There's no POST / PUT / DELETE — RomM owns the lifecycle. + +Requires `collections.read` scope. See the [API Reference](../developers/api-reference.md). From 62fb214a4af1b479c4a22b3af951ed7abfee368d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 18:01:27 +0000 Subject: [PATCH 013/121] docs: draft Wave 2 user-facing feature pages (6 pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second Wave 2 batch — the feature-rich user pages that didn't exist (or were stubs) before. - using/downloads.md: single/bulk/QR/copy-link download paths, the nginx mod_zip streaming story for multi-file + multi-select, DISABLE_DOWNLOAD_ENDPOINT_AUTH guidance with security caveats, client-api-token auth example for programmatic use - using/uploads.md: what/who-can-upload matrix (ROMs, firmware, saves, states, screenshots, manuals, covers), chunked upload mechanics for large files, multi-file upload via zip, post-upload matching notes, permissions summary - using/in-browser-play.md: merged+expanded from EmulatorJS + Ruffle pages. Full platform/core compatibility table, firmware handling, launch paths, controls, in-game menu saves/states/cheats/ screenshots/fullscreen, multi-disc swap, per-core settings, Ruffle details (flash/browser folder only, metadata via Flashpoint) - using/saves-and-states.md: saves-vs-states distinction, upload paths, in-play creation, launch-time selection picker, device sync pointer, format/core compatibility table, RetroAchievements hardcore mode caveat, state-screenshot auto-capture - using/netplay.md: 5.0-reintroduced feature. Hosting/joining flow, ICE server configuration with OpenRelay + Metered recipes, limitations (not all cores, not tournament-grade, same-instance only), nightly CDN caveat, security + cleanup - using/rom-patcher.md: 5.0 feature. Supported patch formats table (IPS/UPS/BPS/PPF/RUP/APS/BDF/PMSR/vcdiff), client-side patching workflow, download vs save-to-library paths, permission gating, typical use cases (translations, hacks, no-intro), browser-memory limits, checksum verification Builds clean with --strict (after fixing two anchor typos found during build). --- docs/using/downloads.md | 90 +++++++++++++++++-- docs/using/in-browser-play.md | 153 +++++++++++++++++++++++++++++++-- docs/using/netplay.md | 102 ++++++++++++++++++++-- docs/using/rom-patcher.md | 94 ++++++++++++++++++-- docs/using/saves-and-states.md | 135 +++++++++++++++++++++++++++-- docs/using/uploads.md | 130 ++++++++++++++++++++++++++-- 6 files changed, 674 insertions(+), 30 deletions(-) diff --git a/docs/using/downloads.md b/docs/using/downloads.md index eaefbdc9..8efc4039 100644 --- a/docs/using/downloads.md +++ b/docs/using/downloads.md @@ -1,10 +1,90 @@ --- -status: placeholder -wave: 2 +title: Downloads +description: Download ROMs from RomM — single, bulk, QR codes, copy-link, and streaming for third-party apps. --- # Downloads -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +## Single download + +The quick path: hover a game card → click **Download**. Or from the game detail page → **Download** button. + +RomM streams the file directly — no temp file on disk, no copy, no waiting for packaging. Large ROMs and multi-disc sets download just as quickly as small ones. + +For multi-file games (folder-based), RomM streams a zip on the fly — see [Multi-file downloads](#multi-file-and-bulk-downloads-nginx-mod_zip) below. + +## Copy download link + +For cases where you want the URL, not the file right now — sending it to another device, pasting into a script, using an external download manager. + +Context menu (…) on a game card → **Copy download link** → URL is on your clipboard. + +Anyone with access to the link and the server can download. By default the link requires your session cookie or a bearer token — see [Third-party download auth](#third-party-download-auth) for the exception. + +## QR code + +For handheld-to-desktop or desktop-to-phone transfers without typing a URL. + +Context menu (…) → **Show QR Code** → point the other device's camera at the screen. + +The QR decodes to the same URL as Copy Link. Same auth rules apply. + +## Multi-file and bulk downloads (nginx `mod_zip`) + +RomM's nginx is built with `mod_zip`, which streams a zip archive over HTTP without ever materialising the file on disk. Two places this matters: + +### Multi-disc / multi-file games + +When a game is stored as a folder (multi-disc, game + DLC, game + patch, etc.), clicking **Download** builds a zip on the fly containing the whole folder. The browser sees a zip download start immediately — no "packaging…" delay. + +### Bulk download from a gallery + +Multi-select ROMs in any gallery (platform view, collection, search results) → toolbar → **Download selected** → single zip with every selected ROM preserved in its platform folder structure. + +No practical limit — the zip is streamed, so memory doesn't grow with selection size. Disk I/O and network bandwidth are the actual limits. + +## Third-party download auth + +Some third-party tools (a dumb emulator loading a ROM by URL, a browser extension, a homebrew Switch app) can't send a bearer token. For those, admins can turn off download-endpoint auth: + +```yaml +environment: + - DISABLE_DOWNLOAD_ENDPOINT_AUTH=true +``` + +This makes ROM and firmware download URLs work unauthenticated. + +!!! danger "Only enable this behind upstream auth" + This flag makes your library world-downloadable from whatever URL RomM lives at. Only set it when you have authentication at the reverse-proxy layer (Authelia, Cloudflare Access, an IP allowlist, a VPN). + +For authenticated programmatic use, a [Client API Token](account-and-profile.md) is the cleaner answer: + +```bash +curl -H "Authorization: Bearer rmm_..." \ + -o mario.sfc \ + https://romm.example.com/api/roms/123/content/mario.sfc +``` + +## Streaming to an emulator + +Some emulators can take an HTTP URL directly — point them at the same URL the Copy Link button produces. With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` and a reverse proxy that restricts access, you can set up truly remote ROM loading from a handheld over Wi-Fi. + +## Download history + +Not tracked in 5.0. RomM doesn't log downloads for privacy reasons — use your reverse proxy's access log if you need to audit. + +## Troubleshooting + +- **Download stalls at N%** — usually the reverse proxy buffering to disk. See [Reverse Proxy → Nginx Proxy Manager](../install/reverse-proxy.md#nginx-proxy-manager) for the `proxy_max_temp_file_size 0` fix. +- **Multi-file zip download is corrupt** — disk may have filled up during streaming, or the nginx mod_zip build is broken. Check `docker logs romm | grep mod_zip`. +- **Bulk download ends early** — reverse proxy is enforcing a request timeout. Raise `proxy_read_timeout` — see [Kubernetes Troubleshooting](../troubleshooting/kubernetes.md#websockets-disconnect-immediately) for nginx-ingress annotation pattern. + +## API + +```http +GET /api/roms/{id}/content/{filename} # single file, stream +GET /api/roms/download?ids=1,2,3 # bulk zip stream +HEAD /api/roms/{id}/content/{filename} # size + content-type without body +``` + +Requires `roms.read` scope unless `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`. Full API reference: [API Reference](../developers/api-reference.md). diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md index 73ce0f3a..83d5778e 100644 --- a/docs/using/in-browser-play.md +++ b/docs/using/in-browser-play.md @@ -1,10 +1,153 @@ --- -status: placeholder -wave: 2 +title: In-Browser Play +description: Play games directly in your browser with EmulatorJS and Ruffle — controls, saves, netplay, fullscreen. --- # In-Browser Play -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +RomM ships with two in-browser emulators: + +- **EmulatorJS** — RetroArch cores compiled to WebAssembly. Covers NES, SNES, Genesis, N64, PSX, PSP, Saturn, and 30+ other platforms. +- **Ruffle** — Flash / Shockwave emulator for browser games. + +Hit the **Play** button on any supported game — the emulator loads full-screen in a new page. Same UI on desktop, mobile, and Console Mode. + +## EmulatorJS + +[EmulatorJS](https://emulatorjs.org/) is a web-based emulator running [RetroArch](https://www.retroarch.com) via [Emscripten](https://emscripten.org/) — so cores you know from RetroArch show up here. + +!!! warning "Emulation is resource-intensive" + Older or less-powerful devices may struggle, especially with demanding cores (Dreamcast, Saturn, PSP). Try a different browser or device before filing a bug. + +!!! note "Some cores don't work in Console Mode" + PSP (`ppsspp`) and MS-DOS (`dosbox-pure`) aren't supported in [Console Mode](console-mode.md). Use the main UI for those. + +### Supported platforms + +| Platform | Cores | +| --- | --- | +| 3DO | `opera` | +| Amiga | `puae` | +| Arcade / MAME | `mame2003_plus`, `mame2003`, `fbneo` | +| Atari 2600 / 5200 / 7800 / Jaguar / Lynx | Various — `stella2014`, `atari800`, `prosystem`, `virtualjaguar`, `handy` | +| Commodore 64 | `vice_x64sc` | +| ColecoVision | `gearcoleco` | +| DOOM | `prboom` | +| Game Boy / Color / Advance | `gambatte`, `mgba` | +| MS-DOS | `dosbox-pure` | +| Neo Geo Pocket / Color | `mednafen_ngp` | +| Nintendo DS | `melonds`, `desmume` | +| Nintendo 64 | `mupen64plus_next`, `parallel_n64` | +| NES / Famicom | `fceumm`, `nestopia` | +| PC-FX | `mednafen_pcfx` | +| PlayStation | `mednafen_psx_hw`, `pcsx_rearmed` | +| PSP | `ppsspp` | +| Sega 32X / CD / Game Gear / Master System / Genesis | `picodrive`, `genesis_plus_gx` | +| Sega Saturn | `mednafen_saturn`, `yabause` | +| SNES / Super Famicom | `snes9x`, `bsnes` | +| TurboGraphx-16 / PC Engine | `mednafen_pce` | +| Virtual Boy | `mednafen_vb` | +| WonderSwan / Color | `mednafen_wswan` | + +Full up-to-date list: [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/). + +### Firmware + +Some platforms need BIOS / firmware — PlayStation, Saturn, Sega CD, PSP, DS. Upload via [Firmware Management](../administration/firmware-management.md) or by dropping files in the right `bios/` folder and rescanning. + +!!! note "Zip bundles for multi-file firmware" + Some cores need several BIOS files. Create a **zip archive** containing all of them and upload that zip through the Firmware tab on the platform detail page. EmulatorJS picks it up as a single firmware bundle. See [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/) for the per-platform file list. + +### Launching a game + +Three paths: + +1. **Game card hover** → **Play** icon. +2. **Game detail page** → **Play** button. +3. **Console Mode** → select game → **A** button. + +If the platform doesn't support in-browser play, Play is replaced with Download. + +### Controls + +EmulatorJS maps keyboard and gamepad automatically. Defaults are core-specific, but approximate the original console layout. Rebind in-game via the **Menu** button during play. + +Operator-level default overrides live in `config.yml` — see [Configuration File → `emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols). + +Keyboard + gamepad can be used simultaneously for multi-player — player 2 on the gamepad, player 1 on the keyboard, for example. + +### Saves and states + +RomM integrates with EmulatorJS so save-files and save-states are loaded and saved automatically: + +- **Before launch** — if multiple saves exist, RomM asks which to load. +- **During play** — any in-emulator save (SRAM flush) or state creation is written straight back to RomM's storage. +- **After quit** — everything is already persisted. No manual download. + +Full details: [Saves & States](saves-and-states.md). + +### Cheats + +In-game Menu → **Cheats** → add manually or load a list. Saved cheats persist per-user per-ROM. RomM doesn't ship a cheat database — you bring your own codes. + +### Screenshots + +During play: in-game Menu → **Screenshot**. Saved to your personal screenshot collection for the ROM. Appears on the Screenshots tab of the game detail page. + +### Fullscreen + +- **F11** — browser fullscreen. +- In-game Menu → **Fullscreen** button — same thing. + +### Multi-disc games + +RomM asks which disc to boot before launching. During play, in-game Menu → **Switch Disc** → pick another disc without exiting (works for cores that support runtime disc swap — PSX, Saturn). + +### Cache / unload behaviour + +Operators can tune how aggressively the emulator caches and unloads via [`emulatorjs.cache_limit` and `emulatorjs.disable_auto_unload`](../reference/configuration-file.md#emulatorjs) in `config.yml`. Defaults are fine for normal use. + +### Full-screen on play + +Profile → User Interface → **Fullscreen on launch** — always enters fullscreen when you hit Play. Handy on TVs. + +### Per-core settings + +In-game Menu → **Settings** — core-specific knobs (vsync, region, sound quality). Your tweaks persist per-core per-user, but operator defaults in [`config.yml`](../reference/configuration-file.md#emulatorjssettings) set the initial values. + +### Netplay + +See [Netplay](netplay.md). One-page deep dive on hosting/joining, ICE servers, and NAT tradeoffs. + +## Ruffle + +[Ruffle](https://ruffle.rs/) is a Flash / Shockwave player in WebAssembly. Useful for preserving the Flash game era. + +!!! important "Ruffle needs the right platform folder" + Ruffle only plays games from platform folders named `flash` or `browser`. If your Flash games are elsewhere, either rename the folder or add a [platform binding](../reference/configuration-file.md#systemplatforms) in `config.yml`. + +### Controls + +Flash games were typically designed for mouse + keyboard — Ruffle passes input through as-is. No controller mapping; gamepad-only users will struggle with most Flash titles. + +### Saves + +Ruffle writes Flash's local-storage to RomM's assets directory. Appears under the game's **Game Data** tab like emulator saves. + +### Supported games + +Most 2D Flash games work. 3D Shockwave and some advanced ActionScript titles may have rendering glitches — see [Ruffle compatibility](https://ruffle.rs/#compatibility). + +### Metadata + +If you enable the [Flashpoint](../administration/metadata-providers.md#flashpoint) provider, Ruffle games pick up descriptions, cover art, and tags from the Flashpoint database. + +## Troubleshooting + +- **Game won't boot** — check firmware is uploaded for platforms that need it. `docker logs romm | grep -i emulator` for server-side hints. +- **Black screen, no audio** — core is incompatible with your browser. Try a different browser (Chrome / Firefox are most tested) or a different core via in-game Menu → **Core**. +- **Controls do nothing** — browser needs focus; click once on the emulator canvas. Some cores need a button press to enumerate gamepads. +- **Netplay "failed to start game"** — see [Netplay troubleshooting](../troubleshooting/netplay.md). +- **DOS game fails to autorun** — try [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjs) in `config.yml`. + +More in [In-Browser Play Troubleshooting](../troubleshooting/in-browser-play.md). diff --git a/docs/using/netplay.md b/docs/using/netplay.md index d5181aa9..f218e3b1 100644 --- a/docs/using/netplay.md +++ b/docs/using/netplay.md @@ -1,10 +1,102 @@ --- -status: placeholder -wave: 2 +title: Netplay +description: Play EmulatorJS games with friends in real time — hosting, joining, ICE servers, and NAT. --- # Netplay -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +**Netplay** lets you play [in-browser](in-browser-play.md) with other users in real time — co-op, turn-based, or party games, shared across the internet. EmulatorJS emulates "two players on one couch with two controllers", streaming video from the host to everyone else. + +Best for 2-player co-op and turn-based games. Not ideal for twitchy fighting games (frame-level input isn't rollback-based). + +!!! note "Netplay is a v5.0 feature" + If you're on an older RomM (4.x), Netplay was disabled with known issues. 5.0 brings it back as a supported feature. + +## Prerequisites + +- EmulatorJS Netplay enabled in `config.yml` (operator-level). +- ICE servers configured (STUN + TURN) — without them, Netplay only works when all players are on the same LAN. +- All players need access to your RomM instance — Netplay doesn't proxy the ROM to people without RomM accounts. + +See [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50) for the operator-side setup. + +## Hosting a room + +1. **Play** a supported game (any EmulatorJS platform works). +2. Once loaded, click the 🌐 (globe) icon in the bottom toolbar. +3. Enter your display name (shown to other players). +4. **Create Room** → optional password. +5. Players who can reach your RomM see the room in the Netplay list. + +You're Player 1. Up to three more players can join as Players 2–4 (core-dependent — some only support 2). + +## Joining a room + +1. Open the same ROM (or any ROM, and open the 🌐 panel). +2. **Available Rooms** lists active rooms on your RomM. +3. Join → enter password if required. +4. Wait for the host to start. You'll see their game once the session's live. + +Other players must be on **your RomM**. Netplay doesn't federate across instances. + +## Controls + +Every player controls their own gamepad / keyboard locally — inputs are sent to the host, which runs the game and broadcasts video. Slight lag is expected over the internet (~50–150 ms typical); higher on transatlantic hops or with TURN in the path. + +## ICE servers (the NAT stuff) + +WebRTC — the protocol Netplay uses — needs help to punch through consumer routers. That's what ICE (STUN + TURN) servers do: + +- **STUN** — cheap; helps two peers find each other's public IPs. Works unless one of you is behind symmetric NAT. +- **TURN** — relays traffic when STUN can't. More resource-intensive; most free TURN services have quotas. + +The operator wires ICE servers in `config.yml`: + +```yaml +emulatorjs: + netplay: + enabled: true + ice_servers: + # Google's free public STUN — no account needed + - urls: "stun:stun.l.google.com:19302" + - urls: "stun:stun1.l.google.com:19302" + # OpenRelay free TURN (rate-limited) + - urls: "turn:openrelay.metered.ca:80" + username: "openrelayproject" + credential: "openrelayproject" + - urls: "turn:openrelay.metered.ca:443" + username: "openrelayproject" + credential: "openrelayproject" +``` + +For production Netplay, operators should get a dedicated TURN account (free tier at [Metered](https://www.metered.ca/stun-turn), or self-host [coturn](https://github.com/coturn/coturn)). + +## Limitations + +- **Not all cores support Netplay.** SNES9x, Mupen64Plus, Mednafen PSX, Genesis Plus GX — generally yes. Cores like PPSSPP or dosbox-pure — no. +- **Frame-perfect fighting isn't realistic.** Netplay is for casual co-op, not tournament-level fighting games. Use something like [FightCade](https://www.fightcade.com/) for that. +- **All players need RomM access.** There's no "join a friend's game without an account" — guests need at least a Viewer account on your instance. +- **RTC over TURN uses real bandwidth.** Hosting a 4-player N64 session over TURN can saturate a modest uplink. Prefer STUN where possible. + +## Known caveat: nightly CDN + +When Netplay is enabled, EmulatorJS loads some assets (localisation, some cores) from the nightly CDN rather than the bundled stable assets. + +```text +https://cdn.emulatorjs.org/nightly/... +``` + +Occasional hiccups (404s, untranslated UI strings) happen when the nightly is out of sync with the stable bundle RomM ships. Usually self-heals on the next RomM image update. If you care about long-term stability and don't need Netplay, leave it off. + +## Security + +Netplay room data is short-lived — no persistent record on RomM beyond the [Netplay Cleanup scheduled task](../administration/scheduled-tasks.md) sweep. Passwords protect room access but aren't stored long-term. + +## Troubleshooting + +- **"Failed to start game"** — Netplay server-side config is broken. Operator: check `docker logs romm | grep -i netplay`. Usually a misconfigured ICE server URL. +- **Other players can't see my room** — either they're not on your RomM (shouldn't happen if they have accounts) or the WebSocket connection is broken. See [WebSocket Troubleshooting](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). +- **Game plays fine locally but Netplay glitches** — network round-trip is too high. Test with Players on the same LAN first; add TURN and re-test from outside. +- **Audio desync** — known WebRTC issue; try a different browser (Chrome is most-tested), or restart the session. + +More in [Netplay Troubleshooting](../troubleshooting/netplay.md). diff --git a/docs/using/rom-patcher.md b/docs/using/rom-patcher.md index 736c48ae..d1355089 100644 --- a/docs/using/rom-patcher.md +++ b/docs/using/rom-patcher.md @@ -1,10 +1,94 @@ --- -status: placeholder -wave: 2 +title: ROM Patcher +description: Apply IPS, UPS, BPS, PPF, and other patch formats to a ROM in the browser. --- # ROM Patcher -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +The **ROM Patcher** applies a patch file (a translation, hack, rebalance, no-intro fix) to a ROM **in your browser** — no CLI, no downloads, no uploading half-patched files by hand. + +New in 5.0. Powered by the `rom-patcher-js` library. + +## Supported patch formats + +| Extension | Purpose | +| --- | --- | +| `.ips` | International Patching System. Oldest + most common. | +| `.ups` | Universal Patching System. Successor to IPS. | +| `.bps` | Binary Patch System. Modern, preferred for ROM hacks. | +| `.ppf` | PlayStation Patch Format. For PSX / Saturn / Dreamcast ISOs. | +| `.rup` | Retroarch Universal Patch. | +| `.aps` | GBA-focused patch format. | +| `.bdf` | Binary diff format. | +| `.pmsr` | Paper Mario Star Rod. | +| `.vcdiff` (`.xdelta`) | Generic binary diff. | + +If your patch has an unusual extension, try renaming to one of the above — many are just different framings of the same underlying algorithm. + +## Getting there + +Menu bar → **Patcher** icon (a wrench). Admins and Editors can also reach it from the ROM detail page → context menu → **Patch this ROM**. + +## Patching workflow + +1. **Upload the ROM** — drag and drop, or click to browse. The Patcher validates basic structure. +2. **Upload the patch** — same drag/drop zone below. +3. **Pick the target platform** — usually auto-detected from the ROM. +4. **Choose output**: + - **Download patched ROM** — saves a file locally; RomM keeps neither the original nor the result. + - **Save to library** — adds the patched ROM as a new entry in your library, alongside the original. + - Optional **filename** — customise the output name. Defaults to ` [patched].`. +5. **Apply**. + +Everything runs client-side in the browser. The ROM never gets uploaded to the RomM server during patching (if you picked Download); nothing leaves your machine. + +## Save to library + +When you pick **Save to library** instead of Download, RomM receives the final patched file and stores it as a new ROM. Goes in the same platform folder as the original, with the patched filename. A subsequent scan picks it up; a **Quick** scan is enough. + +Metadata isn't inherited — the new ROM is **unmatched** until you run a scan. If the patch is listed on IGDB / ROMHacking.net as its own entry, matching may pick it up. Otherwise, match manually or tag with `(igdb-XXXX)` — see [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). + +## Permissions + +| Action | Viewer | Editor | Admin | +| --- | :---: | :---: | :---: | +| Use Patcher → Download | ✓ | ✓ | ✓ | +| Use Patcher → Save to library | — | ✓ | ✓ | + +Saving to the library requires `roms.write` scope. + +## Typical workflows + +### Translation patches + +Many Japanese-only games have fan translations as IPS or BPS patches. Point the Patcher at the original ROM + translation, save to library, and you've got a new library entry alongside the original with the translated game — no clobbering. + +### ROM hacks and rebalances + +Kaizo Mario-type hacks, FF6 balance patches, etc. — same flow. The patched ROM gets its own entry; your original stays intact. + +### Regional fixes + +No-intro patches, region-free patches. Apply, download, and verify with the Hash feature (upcoming) or externally. + +## Limits + +- **Browser memory** — huge ROMs (PSX / Saturn / PSP ISOs, > 1 GB) can struggle in browsers with low memory limits. Safari is the most restrictive; Firefox / Chrome handle bigger files. +- **Multi-file ROMs** — the Patcher operates on a single file. For multi-disc games, patch each ISO separately. +- **Encrypted ROMs** — if the patch was authored against a decrypted ROM (e.g. DS `.nds` vs raw cartridge), your source has to match. +- **Save format changes** — patches that alter save-data layout will invalidate existing save files. Back them up before applying. + +## Verifying the result + +The Patcher shows the pre-patch and post-patch hash (CRC32 + MD5) after apply — compare against whatever the patch author published. If hashes match: you've got the correct patched ROM. If not: wrong source ROM, wrong patch, or the patch archive was truncated. + +## Troubleshooting + +- **"Invalid patch format"** — wrong extension or corrupted file. Try re-downloading the patch. +- **"Checksum mismatch"** — the patch was built against a different ROM revision than yours. Common with no-intro vs TOSEC dumps. Look up the patch author's expected source. +- **Browser hangs on huge ROMs** — close other tabs, try Firefox, or patch outside the browser with `Floating IPS` or `BPS` CLI tools then upload the result. +- **"Save to library" produces a broken ROM** — usually the patcher succeeded but the file was corrupted in transit. Retry; if it persists, patch externally and upload. + +## API + +ROM patching is a client-side feature — no dedicated backend endpoints. When you "save to library", the patched file is uploaded via the standard chunked-upload flow (`POST /api/roms/upload/start` → `PUT /api/roms/upload/{id}` → `POST /api/roms/upload/{id}/complete`). See [Uploads → API](uploads.md#api). diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md index 74cff38e..143096e5 100644 --- a/docs/using/saves-and-states.md +++ b/docs/using/saves-and-states.md @@ -1,10 +1,135 @@ --- -status: placeholder -wave: 2 +title: Saves & States +description: Manage save files and save-states across in-browser play and synced devices. --- # Saves & States -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +Two different things, often confused: + +- **Save files** — the in-game save (`.srm`, `.sav`, `.save`, etc.). What the game writes when you use the in-game save feature. Works across emulator cores that share the format. +- **Save states** — a full memory snapshot of the emulator at a moment in time. Emulator-specific; a SNES9x state won't load in bsnes. + +Both are **per-user per-ROM** and stored under `/romm/assets///`. They follow you across browsers and devices. + +## Where to find them + +Game detail page → **Game Data** tab. Separate sub-tabs for: + +- **Saves** — all save files for this ROM, with date and emulator. +- **States** — all states, with thumbnail (if one was captured). + +## Uploading a save + +Useful for importing from real hardware (Retrode / GB Operator / etc.) or from another emulator. + +1. Game detail → **Game Data** → **Upload Save**. +2. Drop the file. +3. Pick the emulator format if RomM can't infer it from the extension. +4. Save. + +The save is available to [in-browser play](in-browser-play.md) on the next launch. + +## Uploading a state + +Same flow under **Upload State**. Optional screenshot attachment — RomM autogenerates one when you create a state from in-browser play; only matters when uploading from outside. + +## Creating during play + +In-game Menu in EmulatorJS: + +- **Save** — writes in-game save data back to RomM. Same as the native console's save-to-cartridge flow. +- **Save State** / **Load State** — creates a full memory snapshot, or restores from one. RomM uploads the snapshot automatically. + +There's no "forgot to upload" step — everything persists as soon as you do it in-emulator. + +## Selecting on launch + +If a ROM has multiple saves or states, the pre-launch picker appears before the emulator loads: + +- **Save file** dropdown — which save to load on boot. +- **State** dropdown — optional; loads immediately after boot. + +[Console Mode](console-mode.md) surfaces the same picker on a larger gamepad-friendly dialog. + +## Downloading + +For using a save on real hardware or another emulator elsewhere: + +**Game Data → Saves → Context menu (…) → Download**. + +Or bulk-download everything for a ROM by multi-selecting → toolbar → Download. + +## Deleting + +**Game Data → Saves/States → Context menu → Delete**. Removes both the DB row and the file. Can't be undone. + +For bulk cleanup (e.g. "delete every state for games I've beaten"), use the multi-select toolbar. + +## Device sync + +Saves and states can sync to/from registered devices (Grout on muOS, DeckRommSync on a Deck, etc.). Covered in depth in the ecosystem section: + +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md) — wire-level reference. +- [SSH Sync](../administration/ssh-sync.md) — operator-side config. +- [Argosy Launcher](../ecosystem/argosy.md) / [Grout](../ecosystem/grout.md) — per-app setup. + +From the end-user side: once your device is paired and sync is running, saves made on the device appear in RomM within a couple of sync cycles (default: 15 minutes). Conflicts — same ROM saved on two devices between syncs — surface as two separate save entries; pick which to keep. + +## Format / core compatibility + +### Saves + +Save files are usually format-interchangeable across cores for the same platform, but not always. + +| Platform | Format | Usually-compatible | +| --- | --- | --- | +| NES | `.sav` | Yes, across FCEUmm / Nestopia | +| SNES | `.srm` | Yes, across SNES9x / bsnes | +| Genesis / Mega Drive | `.srm` | Yes | +| Game Boy / GBC / GBA | `.sav` | Yes, across Gambatte / mGBA | +| N64 | `.srm`, `.eep`, `.fla` | Yes, but per-type — the right file must be uploaded | +| PSX | `.srm` (memory card) | Yes, across Mednafen / PCSX cores | +| Saturn / Dreamcast | Varies | Check core docs | + +If you're moving saves between RomM's EmulatorJS and a stand-alone emulator (RetroArch, Dolphin, PPSSPP), usually works for mainline cores. + +### States + +Save states are **always** core-specific. A SNES9x state will not load in bsnes. If you switch cores, your states become useless. + +Practical advice: stick to one core per platform if you use states heavily, or use save files (which are interchangeable) as your primary persistence. + +## RetroAchievements and states + +If you use [RetroAchievements](retroachievements.md) in hardcore mode, loading a save state **disables achievement tracking** for that session. RomM doesn't enforce this, but the RA server will stop crediting achievements. Use save files (the in-game save) instead of states if you care. + +## Screenshots with states + +When you create a state via in-game Menu → Save State, EmulatorJS grabs a frame as the state's thumbnail. Shows up in the **States** list and on the launch picker. + +## Troubleshooting + +- **Save uploaded but the game doesn't see it** — wrong format for the core. Check the compatibility table above; re-upload or switch cores. +- **State loads a corrupted frame** — state was saved by a different build of the core. If RomM updated the emulator bundle, old states may not load cleanly. Re-create or start a fresh save. +- **Save disappears after play** — the emulator didn't flush on quit. Use in-game **Save and Quit** instead of just closing the browser. +- **Can't upload — "file too large"** — reverse proxy limit; raise `client_max_body_size` / `proxy-body-size`. See [Reverse Proxy](../install/reverse-proxy.md). + +More in [Troubleshooting](../troubleshooting/index.md). + +## API + +```http +POST /api/saves # upload +GET /api/saves # list own saves +GET /api/saves/{id}/content # download +PUT /api/saves/{id} # update metadata / attach screenshot +POST /api/saves/delete # bulk delete + +POST /api/states # upload +GET /api/states # list own states +PUT /api/states/{id} # update +POST /api/states/delete # bulk delete +``` + +Requires `assets.read` / `assets.write` scopes. Full reference: [API Reference](../developers/api-reference.md). diff --git a/docs/using/uploads.md b/docs/using/uploads.md index bf92838a..0c03b5fe 100644 --- a/docs/using/uploads.md +++ b/docs/using/uploads.md @@ -1,10 +1,130 @@ --- -status: placeholder -wave: 2 +title: Uploads +description: Upload ROMs, firmware, saves, states, and screenshots into RomM from the web UI. --- # Uploads -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +## What you can upload + +| Type | Permission | Where | Details | +| --- | --- | --- | --- | +| **ROMs** | Admin, Editor | **Upload** in menu bar; **Upload ROM** on platform detail; ROM detail → Upload menu | Goes to the correct platform folder in `/romm/library`. | +| **Firmware / BIOS** | Admin, Editor | Platform detail → Firmware button → Upload | See [Firmware Management](../administration/firmware-management.md). | +| **Saves** | Self (own games) | Game detail → Game Data tab → Upload Save | Per-ROM, per-user. | +| **States** | Self (own games) | Game detail → Game Data tab → Upload State | Per-ROM, per-user. Optional screenshot attached. | +| **Screenshots** | Self (own games) | Game detail → Screenshots tab → Upload | Per-ROM, per-user. | +| **Manuals** | Admin, Editor | Game detail → Manual tab → Upload (when empty) | PDF; becomes the Manual tab content. | +| **Cover art** | Admin, Editor | Game detail → Edit → Upload cover | Replaces the provider-fetched cover. | + +## ROM upload + +### The Upload page + +1. **Upload** in the menu bar. +2. Pick a target platform. +3. **+ ADD** → select files, or drag-and-drop onto the upload zone. +4. Click **Upload**. + +Multiple files upload in parallel. Progress bars show per-file status; failures surface with an error per file rather than stopping the whole batch. + +### Large uploads (chunked) + +Files over 64 MB are uploaded in chunks. RomM: + +1. Opens an upload session (`POST /api/roms/upload/start`). +2. Streams chunks (`PUT /api/roms/upload/{id}`) — 64 MB each. +3. Finalises the upload (`POST /api/roms/upload/{id}/complete`), which assembles the file and indexes it. + +A browser refresh mid-upload cancels the session and cleans up partial data. + +### Multi-file uploads + +To upload a multi-file game (multi-disc, game + DLC): + +1. Zip the whole game folder on your end. +2. Upload the zip — RomM detects the structure and unpacks to a folder under the platform directory. +3. The resulting folder follows the [multi-file game convention](../getting-started/folder-structure.md#multi-file-games). + +### After upload + +Uploaded ROMs are immediately visible in the platform view, but aren't **matched** to metadata until the next scan. Two options: + +- **Run a Quick scan** — picks up new files only. +- **Match manually** — game detail → Match → search for the title. + +## Save uploads + +For your own games. Useful for importing existing save files from another emulator or from the original console. + +1. Open the game → **Game Data** tab → **Upload Save**. +2. Drag / browse the save file. +3. Optionally attach a screenshot (a thumbnail for the save). +4. Save. + +The file's stored under `/romm/assets///saves/` and is available to your [in-browser play](in-browser-play.md) sessions. RomM doesn't rewrite the file contents — it's stored as-is. + +See [Saves & States](saves-and-states.md) for more. + +## State uploads + +Emulator save-states. Same flow as saves but under **States**. + +- State formats are emulator-specific — a SNES9x state won't load in bsnes. +- Screenshots can be attached — RomM generates one automatically from the emulator if you create a state from [in-browser play](in-browser-play.md). + +## Screenshot uploads + +Personal screenshot uploads attach to a ROM (not to a save/state). Great for building a screenshot gallery per game. + +Game detail → **Screenshots** tab → **Upload**. + +Supported formats: PNG, JPEG, WebP. RomM converts on ingest to WebP via the Image Conversion task — see [Scheduled Tasks](../administration/scheduled-tasks.md). + +## Firmware uploads + +Admin / Editor only. See [Firmware Management](../administration/firmware-management.md) for the full flow — two paths (drop in folder + scan, or UI upload), per-platform organisation, file naming requirements. + +## Manual uploads + +Admin / Editor. Game detail → **Manual** tab → **Upload Manual** (button appears when no manual is set). + +Supported: PDF. Renders in the browser via the Manual tab. + +## Cover art uploads + +Admin / Editor. Game detail → Context menu (…) → **Edit** → **Upload cover** → PNG / JPG / WebP. Overrides provider-fetched cover. To revert: Edit → **Reset cover** → the next metadata refresh repopulates from providers. + +## Permissions summary + +| Action | Viewer | Editor | Admin | +| --- | :---: | :---: | :---: | +| Upload save/state/screenshot for own account | ✓ | ✓ | ✓ | +| Upload ROMs | — | ✓ | ✓ | +| Upload firmware | — | ✓ | ✓ | +| Upload manual / cover art | — | ✓ | ✓ | + +Full scope matrix in [Users & Roles](../administration/users-and-roles.md#scope-matrix). + +## Troubleshooting + +- **`413 Request Entity Too Large`** — your reverse proxy or ingress is capping body size. See [Reverse Proxy](../install/reverse-proxy.md) for the `client_max_body_size 0` / `proxy-body-size: "0"` fix. +- **Upload progresses then fails at 99%** — the finalise step timed out. Usually reverse-proxy read timeout is too tight; raise it. +- **"File is not a valid ROM for this platform"** — RomM's extension check rejected the file. Either it's the wrong platform, or the extension is obscure — see [Folder Structure → Naming](../getting-started/folder-structure.md#naming-convention). +- **Save upload silently doesn't appear in-emulator** — you uploaded to the wrong emulator core. Check [Saves & States → Emulator compatibility](saves-and-states.md) for the format → core matrix. + +## API + +```http +POST /api/roms/upload/start # start a chunked ROM upload +PUT /api/roms/upload/{upload_id} # chunk +POST /api/roms/upload/{upload_id}/complete +POST /api/roms/upload/{upload_id}/cancel + +POST /api/saves # save file upload +POST /api/states # state upload +POST /api/screenshots # screenshot upload +POST /api/firmware # firmware upload +``` + +Requires appropriate `*.write` scopes. See [API Reference](../developers/api-reference.md). From 798736637da645974860c887ae3272a794d8d254 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 18:08:39 +0000 Subject: [PATCH 014/121] docs: finish Wave 2 using/ section + three new troubleshooting pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wraps up the Wave 2 using/ bucket (17/17) and the Wave 2 troubleshooting additions (3/3). - using/retroachievements.md: linking an RA account, sync cadence, hardcore mode + save-state caveat, hash-based matching requirement, privacy / unlinking, API surface - using/pwa.md: install on Android / iOS / desktop (Chrome, Edge, Safari, Firefox-via-extension), HTTPS requirement, update flow, known iOS limitations, Console Mode combo - using/mobile-and-tv.md: form-factor matrix (phones, tablets, TVs, set-top boxes, handhelds, Steam Deck), native-app vs PWA guidance, bandwidth table, Tailscale + Cloudflare Access recommendations for remote access - using/account-and-profile.md: username/email/password/avatar, Client API Tokens with 25-cap and device pairing flow, keyboard shortcuts, self-deletion policy, OIDC quirks - using/languages.md: full 19-locale table, contributing a new locale or filling gaps in an existing one, RTL caveat, CJK typography note - troubleshooting/in-browser-play.md: EmulatorJS + Ruffle diagnostics — slim-image gotcha, cross-origin isolation, firmware-required messages, performance tips per mobile device, core-specific known issues table - troubleshooting/netplay.md: "Failed to start game" walkthrough, room visibility, lag / desync, audio crackle, nightly CDN 404s, chrome://webrtc-internals debugging pointer - troubleshooting/sync.md: API-sync vs SSH-sync diagnostics, token-invalid causes, pairing-code failures, SSH permission denied + host-key-mismatch + key-permission errors, play sessions missing Builds clean with --strict (after fixing one anchor typo found). --- docs/troubleshooting/in-browser-play.md | 116 +++++++++++++++++++- docs/troubleshooting/netplay.md | 87 ++++++++++++++- docs/troubleshooting/sync.md | 128 +++++++++++++++++++++- docs/using/account-and-profile.md | 139 +++++++++++++++++++++++- docs/using/languages.md | 94 +++++++++++++++- docs/using/mobile-and-tv.md | 115 +++++++++++++++++++- docs/using/pwa.md | 114 ++++++++++++++++++- docs/using/retroachievements.md | 103 +++++++++++++++++- 8 files changed, 853 insertions(+), 43 deletions(-) diff --git a/docs/troubleshooting/in-browser-play.md b/docs/troubleshooting/in-browser-play.md index 73ce0f3a..bfb600fb 100644 --- a/docs/troubleshooting/in-browser-play.md +++ b/docs/troubleshooting/in-browser-play.md @@ -1,10 +1,114 @@ --- -status: placeholder -wave: 2 +title: In-Browser Play Troubleshooting +description: Diagnose EmulatorJS and Ruffle issues — cores not loading, BIOS missing, audio, performance. --- -# In-Browser Play +# In-Browser Play Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +## EmulatorJS won't load at all + +Symptom: Play button spins forever or shows an error immediately. + +Checks in order: + +1. **Is it the slim image?** `ENABLE_EMULATORJS=true` on the slim image doesn't magically include EmulatorJS. Either switch to `rommapp/romm:` (full) or disable in-browser play. See [Image Variants](../install/image-variants.md). +2. **Browser console** — open devtools, look for 404s on `/assets/emulatorjs/...`. Indicates the EmulatorJS bundle didn't install correctly in the container. `docker logs romm` may show the entrypoint's install step failing. +3. **Browser compatibility** — EmulatorJS uses SharedArrayBuffer. Needs a modern Chrome/Firefox/Safari and an HTTPS-served RomM (cross-origin isolation requires HTTPS). If you're still on plain HTTP for some reason, TLS it — see [Reverse Proxy](../install/reverse-proxy.md). + +## Black screen, no audio + +EmulatorJS loaded, but the game is dead. + +- **Core incompatibility.** Some cores have issues with specific ROMs. Try a different core via in-game Menu → **Core**. +- **ROM dump is bad.** A broken dump produces a black screen on most emulators. Try a known-good No-Intro / Redump version. +- **Browser doesn't support WebGL.** Rare on modern browsers. Check `chrome://gpu` / `about:support` for WebGL status. + +## "Firmware required" + +The platform needs BIOS files RomM doesn't have. See [Firmware Management](../administration/firmware-management.md) for how to upload. + +Specific examples: + +- PlayStation → `scph1001.bin` (or region equivalents). +- Game Boy Advance → `gba_bios.bin`. +- Saturn → `saturn_bios.bin` + CD BIOS. +- Nintendo DS → `firmware.bin` + `bios7.bin` + `bios9.bin`. + +Not sure which files? Error message usually names them. Or check [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/). + +Multi-file firmware — zip them together and upload as a single `firmware.zip`. EmulatorJS unpacks automatically. + +## Controls do nothing + +- **Browser needs focus.** Click the emulator canvas once. Some cores need a button press to enumerate gamepads. +- **Gamepad not detected.** Chrome sometimes requires a button press on the page before it registers a gamepad. Press something on the pad. +- **Conflicting gamepad.** You have two gamepads plugged in and EmulatorJS is talking to the wrong one. Unplug the extras. +- **Keyboard mapping overrides.** In-game Menu → **Controls** → **Reset to Defaults**. + +## Audio stutters / desyncs + +- **Underpowered device.** Cheap TV boxes and old phones struggle. Try a lighter core (e.g. `snes9x` instead of `bsnes`), or accept the stutter. +- **Tab is backgrounded.** Most browsers throttle background tabs. Keep the play tab foregrounded. +- **Other tabs eating CPU.** Close them. +- **Specific to a core.** `mednafen_saturn` is notorious; try `yabause` if Saturn audio is bad. + +## Save states crash on load + +- **Core changed between save and load.** States are emulator-build-specific. If RomM updated and your state was saved against the old build, it likely won't load. Start fresh, or switch to in-game saves (portable across builds). +- **Cheats on during save, off during load.** Or vice versa. Toggle to match. + +## In-game Menu won't open + +Different cores use different hotkeys: + +- **PC keyboard** — often F1, F9, or `~` (tilde). Some cores use ESC. +- **Gamepad** — usually Select + Start simultaneously. + +Rebind via Profile → User Interface (the operator-side overrides live in [`emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols) in `config.yml`). + +## DOS games fail to boot + +- **autorun.bat issues** — turn on [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjsdisable_batch_bootup) in `config.yml`. +- **Sound card config wrong** — dosbox-pure tries to auto-detect; may need manual tweaking. In-game Menu → **Settings** → set Sound Blaster port. +- **Game needs specific CPU speed** — some DOS games are CPU-bound. Slow down via dosbox-pure settings. + +See the [MS-DOS platform guide](../platforms/ms-dos.md) for deeper DOS-specific notes. + +## Ruffle games + +- **"File not Flash"** — confirm the file extension is `.swf`. Ruffle only handles Flash SWF. +- **Wrong platform folder.** Ruffle only plays from `flash/` or `browser/` folders. See [Ruffle setup](../using/in-browser-play.md#ruffle). +- **AS3 game crashes** — Ruffle's ActionScript 3 support is in progress. Some games won't work cleanly yet. [Ruffle compatibility list](https://ruffle.rs/#compatibility). + +## Performance on mobile + +In-browser emulation is CPU-heavy. Mobile tips: + +- **Plug the device in.** Batteries throttle. Playing plugged in gets you ~30% more performance. +- **Use Chrome on Android, Safari on iOS.** Other browsers are measurably slower for WASM. +- **Close other tabs.** +- **Try a lighter core.** `snes9x` over `bsnes`, `fceumm` over `nestopia`, etc. +- **Consider a native app.** [Argosy Launcher](../ecosystem/argosy.md) on Android uses native emulators — orders of magnitude more efficient. + +## Fullscreen oddities + +- **Notch / cutout clipping on mobile** — iPhone notches and Android cutouts can eat corners of the emulator viewport. Rotate to landscape before going fullscreen, or toggle Profile → User Interface → Fullscreen strategy. +- **Browser escapes fullscreen after idle** — browser security behaviour. Can't be worked around. + +## Known limitations by core + +| Core | Known issues | +| --- | --- | +| `ppsspp` (PSP) | Not supported in Console Mode. | +| `dosbox-pure` (DOS) | Not supported in Console Mode. Some DOS quirks; see `disable_batch_bootup`. | +| `mednafen_saturn` | Audio stutter on weak hardware. `yabause` is an alternative. | +| `opera` (3DO) | Niche. Limited ROM compatibility. | +| All Nintendo DS cores | Touchscreen emulation is imperfect on non-touch devices. | + +## Still stuck + +- `docker logs romm | grep -i emulator` — server-side clues. +- Browser devtools Console — client-side clues. +- [Discord](https://discord.gg/romm) `#help` — include the ROM file, the core you tried, the exact error. + +For Netplay-specific issues: [Netplay Troubleshooting](netplay.md). diff --git a/docs/troubleshooting/netplay.md b/docs/troubleshooting/netplay.md index d5181aa9..e9ecf2b9 100644 --- a/docs/troubleshooting/netplay.md +++ b/docs/troubleshooting/netplay.md @@ -1,10 +1,85 @@ --- -status: placeholder -wave: 2 +title: Netplay Troubleshooting +description: Fix EmulatorJS Netplay — rooms not appearing, failed to start, desync, lag. --- -# Netplay +# Netplay Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +Netplay uses WebRTC + ICE servers for peer-to-peer connections. Most issues are ICE-related. + +## `Failed to start game` + +The most common error. Almost always the operator-side config: + +1. **Is Netplay enabled?** `emulatorjs.netplay.enabled: true` in `config.yml`. +2. **Are ICE servers configured?** The operator needs at least one STUN server in `emulatorjs.netplay.ice_servers`. Without any, NAT traversal can't begin. +3. **ICE server URLs reachable?** RomM can't talk to `stun.l.google.com:19302` if your server has no outbound internet; sounds silly but happens in air-gapped labs. + +Operator: check `docker logs romm | grep -i netplay` — errors usually point at ICE config or network access. + +Full config: [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50). + +## Can't see the room + +You created a room as host; other players don't see it. + +- **They need RomM accounts on your instance.** Netplay doesn't federate. A user with no account or on a different RomM can't see or join. +- **WebSocket connection is broken.** Open devtools → Network → WS tab. If Socket.IO is disconnecting, see [Authentication Troubleshooting → WebSockets](authentication.md#400-bad-request-on-the-websocket-endpoint). +- **Other player didn't open the Netplay panel.** They need to click the 🌐 icon on the emulator toolbar to see the room list. + +## Joined but video never appears + +- **Host's browser stopped streaming.** Check host's tab is still foregrounded; browsers throttle background tabs. +- **NAT traversal failed.** STUN couldn't hole-punch, and you have no TURN configured. Add a TURN server — see [Netplay → ICE servers](../using/netplay.md#ice-servers-the-nat-stuff). +- **Symmetric NAT on one end.** Corporate networks and some carrier-grade NAT can't be STUN-traversed. Only TURN (relay) will work. + +## High lag / input delay + +Normal Netplay delay is 50–150 ms. More than that: + +- **You're routing through TURN.** TURN relays add latency proportional to your distance from the TURN server. Pick a TURN server geographically close to both players, or set up your own [coturn](https://github.com/coturn/coturn). +- **Host has slow uplink.** The host is streaming video to everyone; if upload bandwidth is tight, lag spikes. Test host's upload with `speedtest-cli`. +- **Weak host CPU.** The host runs the emulator AND encodes the video stream. A Raspberry Pi hosting N64 Netplay is going to struggle. + +## Desync (players see different game state) + +Different players' screens diverge over time — you do a move, they don't see it. + +- **Browser tab backgrounded.** Browsers throttle. Keep both tabs foregrounded. +- **Bandwidth starvation.** Video stream is dropping frames, inputs are queueing. Reduce resolution or lower the emulator frame rate. +- **Core-specific bug.** Not all cores handle Netplay cleanly. `snes9x` and `genesis_plus_gx` are most reliable; some 64-bit era cores have known desync bugs. + +## Audio crackle / cuts out on the client side + +WebRTC audio is known to be fragile. Workarounds: + +- **Use Chrome.** Its WebRTC implementation is the most mature. +- **Lower audio quality** in the in-game Menu → Audio. +- **Restart the session** — audio sometimes recovers only after a full room teardown. + +## Room disappears after the host leaves + +Expected. Rooms are ephemeral — when the host disconnects, the room is cleaned up (either immediately or by the next [Netplay Cleanup scheduled task](../administration/scheduled-tasks.md) sweep). + +If you want your session back, the host recreates the room and other players rejoin. + +## `nightly CDN 404` or `localization file not found` + +Netplay switches EmulatorJS to nightly-CDN assets. Sometimes the nightly is temporarily broken. + +- **Temporary** — usually self-heals within a day. +- **Workaround** — operator can disable Netplay (`emulatorjs.netplay.enabled: false`) to go back to stable local assets. + +See [In-Browser Play → Netplay](../using/netplay.md#known-caveat-nightly-cdn). + +## Still stuck + +- `docker logs romm | grep -i netplay` — server-side log. +- Browser devtools Console — client-side log. +- `chrome://webrtc-internals` — live ICE / WebRTC stats on Chrome. Shows exactly where ICE is failing. +- [Discord](https://discord.gg/romm) `#help` with both sides' error text. + +## See also + +- [Using → Netplay](../using/netplay.md) — how Netplay works and how to configure it. +- [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50) — full config schema. diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md index ab83d5e3..b469cd8f 100644 --- a/docs/troubleshooting/sync.md +++ b/docs/troubleshooting/sync.md @@ -1,10 +1,126 @@ --- -status: placeholder -wave: 2 +title: Device Sync Troubleshooting +description: Diagnose push-pull sync, Client API Token pairing, and companion-app connectivity. --- -# Device Sync +# Device Sync Troubleshooting -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +Device Sync covers two distinct things: + +- **API sync** (HTTPS, token-auth) — used by most companion apps. Argosy, Playnite, RommBrowser, mobile apps. +- **SSH sync** (push-pull task) — used by handhelds. Grout on muOS, DeckRommSync on Deck, etc. + +Troubleshooting paths differ. + +## API-based sync (companion apps) + +### App says "token invalid" + +- **Token revoked.** Check Profile → Client API Tokens. Was it deleted? +- **Token expired.** Tokens can have optional expiry — check the expiry date on the token page. +- **Scopes mismatch.** App needs scopes the token doesn't carry. Create a new token with the required scopes. See [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix). +- **User downgraded.** If the owning user got demoted (Admin → Viewer, say), tokens with scopes outside the new role stop working. + +### App can't reach RomM + +- **URL wrong.** Double-check what you entered in the app config. `https://romm.example.com`, not `https://romm.example.com/api` (the app appends the API path). +- **TLS cert issues.** Self-signed certs cause problems. Use a proper cert (Let's Encrypt through your reverse proxy) or configure the app to skip cert validation (not recommended). +- **Firewall.** From the device, `curl https://romm.example.com/api/heartbeat` — should return JSON. If not, network path is blocked. + +### Pairing code doesn't work + +The pairing flow: RomM generates a short code, device exchanges it for a full token. + +- **Code expired.** Codes are short-lived (5 minutes default). Generate a new one. +- **Code already used.** Single-use. Generate a new one. +- **Device can't reach RomM.** Pairing still requires network access from the device to RomM's API. +- **User out of token slots.** 25-token max per user. Revoke an unused token first. + +Full flow: [Client API Tokens](../ecosystem/client-api-tokens.md). + +### Saves aren't appearing after playing on the device + +- **Sync hasn't run yet.** The Push-Pull Device Sync task runs every 15 minutes by default. Check Administration → Tasks for last-run timestamp. +- **App didn't upload.** Open the app's logs. Some apps upload on app-close rather than on every save. +- **Wrong save location on device.** RomM reads from a specific path; if the app stored saves elsewhere, they won't sync. Check the device's save path configuration. + +## SSH-based sync (handhelds) + +### Permission denied (publickey) + +RomM's SSH key isn't authorised on the device. + +1. Verify the **public** key (from `~/romm-sync-key.pub` on the host) is in the device's `~/.ssh/authorized_keys`. +2. Check line breaks — CRLF vs LF issues bite here. The `authorized_keys` file should have one key per line, Unix line endings. +3. Check file permissions on the device: `~/.ssh/` should be `700`, `~/.ssh/authorized_keys` should be `600`. + +See [SSH Sync → Configuring a device](../administration/ssh-sync.md#configuring-a-device). + +### Host key verification failed + +The device's SSH host key changed (usually after a reinstall / reflash). + +Fix from inside the RomM container: + +```sh +docker exec romm ssh-keygen -R +``` + +Next sync cycle will accept the new host key. + +### Can't read SSH key in container + +```text +Permissions 0644 for '/romm/.ssh/id_rsa' are too open +``` + +OpenSSH refuses to use keys with loose permissions. On the host: + +```sh +chmod 600 /path/to/romm-sync-key +``` + +And verify the Docker bind mount isn't forcing different perms (ro is fine; mode-reset-via-mount-option is the usual culprit). + +### Sync task doesn't run + +- **`SSH_PRIVATE_KEY_PATH` not set.** Check the RomM container's env. If the path doesn't exist inside the container, sync quietly no-ops. +- **No devices registered.** Administration → Devices — if empty, there's nothing to sync. +- **Cron expression wrong.** Default `PUSH_PULL_SYNC_INTERVAL_CRON=*/15 * * * *`. Invalid cron silently doesn't run. +- **Task failing silently.** `GET /api/tasks/status` — status shows per-task state. "failed" with a last-error is a direct pointer. + +## Play sessions + +Play sessions from a companion app (time-played tracking, Continue Playing ribbon): + +- **Not appearing in RomM** — app isn't POSTing to `/api/play-sessions`. Check app's logs. +- **Duplicate sessions** — app is re-posting on sync. Known edge case for some companion apps; usually harmless. +- **Times look wrong** — timezone mismatch between device and RomM. RomM stores UTC; display converts to user TZ. + +## Devices panel says "offline" + +RomM sees no heartbeat from the device. Either: + +- Device is genuinely off. +- Device's companion app has stopped phoning home. Check the app's logs / relaunch it. +- RomM last-seen threshold elapsed. Devices show online for 1 hour after last heartbeat; after that, offline. + +## Smart collection / filter isn't updating after sync + +Smart collections re-evaluate on ROM / metadata change. A pure save upload doesn't trigger re-eval. If you have a Smart Collection tied to playtime, it updates on the next play session ingest. + +Admins can force a re-eval via Administration → Tasks → Refresh Smart Collections. + +## Still stuck + +- **API sync**: `docker logs romm | grep -iE 'sync|device|token'`. +- **SSH sync**: `docker logs romm | grep -iE 'push_pull|ssh'`. +- Device-side logs from the companion app. Each app differs. +- [Discord](https://discord.gg/romm) `#help` — include the app name, its version, and the RomM log lines. + +## See also + +- [Client API Tokens](../ecosystem/client-api-tokens.md) — token + pairing flow reference. +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md) — wire-level protocol details. +- [SSH Sync](../administration/ssh-sync.md) — server-side SSH sync config. +- [Companion apps](../ecosystem/community-apps.md) — list of integrations and their status. diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index 30d09b25..76f04f22 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -1,10 +1,139 @@ --- -status: placeholder -wave: 2 +title: Account & Profile +description: Manage your RomM account — profile, password, avatar, API tokens, device pairing. --- # Account & Profile -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +Your account controls: profile drawer → **Profile**. + +Every user (Viewer, Editor, Admin) can manage their own profile. Admins can edit *other* users' profiles; see [Users & Roles](../administration/users-and-roles.md) for the admin side. + +## Profile basics + +### Username and email + +Editable from the profile page. Changing your email: + +- **OIDC users** — email is usually governed by the IdP. RomM lets you change it here but OIDC login will re-match on the IdP-supplied email at next login. +- **Local users** — change freely. The new email is used for password reset (if email transport is configured — see [Authentication](../administration/authentication.md)). + +### Password + +For local accounts: **Current password** + **New password** (twice). RomM uses bcrypt — no plaintext storage, no password recovery by the admin (admins reset by setting a new password; they can't see your old one). + +OIDC users don't have local passwords — authentication is via the IdP. Password fields are hidden on the profile page. + +### Avatar + +Upload a PNG / JPEG / WebP. Displayed next to your name across the UI. + +### Preferred username in OIDC + +If you're an OIDC user and want RomM to show your `preferred_username` from the token instead of your email local-part, the operator can set `OIDC_USERNAME_ATTRIBUTE=preferred_username` — see [OIDC Setup](../administration/oidc/index.md). + +## Client API tokens + +Long-lived API tokens for companion apps, scripts, and integrations. Each token is scoped to a subset of your user's scopes, and optionally expires. + +See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md) — this section is the "how I create one from the UI" version. + +### Creating a token + +1. **Profile → Client API Tokens → + New Token**. +2. Pick: + - **Name** — descriptive (e.g. "Grout on my RG35XX"). + - **Scopes** — which permissions to include. Default is read-only; tick write scopes deliberately. + - **Expiry** — optional. Blank = never expires. +3. **Create**. +4. The token appears **once**. Copy it immediately — you can't retrieve it later. + +Token format: `rmm_` + 40 hex chars. Treat it like a password. + +### Pairing to a device + +For handheld apps like Grout, typing a 44-character token on a thumbstick isn't realistic. Instead: + +1. **Profile → Client API Tokens → [token] → Pair Device**. +2. A short numeric code appears (8 digits, valid for 5 minutes). +3. On the device (Grout app, etc.): enter the code. +4. The device exchanges the code for the full token via the pairing API. +5. Done — token is on the device, you never typed it. + +Full flow in [Client API Tokens](../ecosystem/client-api-tokens.md). + +### Limits + +- **25 active tokens per user.** Delete old ones to free slots. +- **Tokens carry a subset of your scopes.** If an admin demotes you to Viewer, any token you hold with admin scopes stops working — tokens don't escalate privileges beyond the owning user's current role. + +### Revoking + +**Profile → Client API Tokens → [token] → Revoke.** Takes effect immediately. The companion app loses access on its next API call. + +## Keyboard shortcuts + +Available throughout the app: + +| Key | Action | +| --- | --- | +| `/` | Focus the search bar. | +| `Esc` | Close dialog, drawer, or player. | +| `g h` | Go to home dashboard. | +| `g s` | Open search page. | +| `g c` | Focus the scan / admin action in the sidebar. | +| `j` / `k` | Next / previous item in a list view. | +| `Enter` | Activate focused item. | + +## Personal data + +Per-game data you set (ratings, notes, status, playtime) is all stored per-user. Admins can't see your per-game ratings unless you share them via a public collection. + +To export your personal data (GDPR-ish use case): + +```http +GET /api/users/me?include=personal +Authorization: Bearer +``` + +Returns your user record plus a `personal_data` array of per-ROM ratings, notes, and play sessions. Full API details in [API Reference](../developers/api-reference.md). + +## Deleting your account + +Self-deletion isn't available in 5.0. Ask an admin to delete your account — they can do it from **Administration → Users → [your account] → Delete**. + +When deleted: + +- Your profile is removed. +- Your saves, states, screenshots, personal ROM data (ratings, notes, play sessions) are removed. +- Your **public collections** stay (they belong to the community), but show as orphaned. +- Your **private collections** are deleted. +- Your **Client API Tokens** are revoked. + +## OIDC-specific notes + +If you signed in via OIDC: + +- Most fields are still editable on the RomM side. Email and role may be overwritten on next login based on IdP claims. +- **Logging out of RomM doesn't log out of the IdP** unless the operator has enabled `OIDC_RP_INITIATED_LOGOUT` — see [OIDC Setup](../administration/oidc/index.md). +- Unlinking OIDC isn't currently supported from the profile page. If you want to convert to a local account, ask an admin to delete your OIDC-linked account, then re-register with a local email+password. + +## Language and UI preferences + +Lives on a separate page — **Profile → User Interface** — not here. Covers: + +- [Language](languages.md) — 19 locales. +- Theme (Dark / Light / Auto). +- Home dashboard ribbons. +- Collection types (toggle [virtual collections](virtual-collections.md) dimensions). +- Game card layout (cover style, 3D tilt, density). +- Full-screen on launch. + +See those pages for detail. + +## Troubleshooting + +- **"Current password incorrect"** — caps lock, or the password was changed by an admin. Ask them. +- **Avatar upload silently fails** — file is too large; RomM accepts up to a few MB. Resize. +- **Can't create API token** — you've hit the 25-token cap. Revoke old ones. +- **OIDC login came back as Viewer when I'm an Admin** — role claim mapping is misconfigured or missing. See [OIDC Troubleshooting → Role mapping](../troubleshooting/authentication.md#user-is-created-but-stays-viewer-even-though-they-should-be-admin). diff --git a/docs/using/languages.md b/docs/using/languages.md index 5d267fb4..b271ac21 100644 --- a/docs/using/languages.md +++ b/docs/using/languages.md @@ -1,10 +1,94 @@ --- -status: placeholder -wave: 2 +title: Languages +description: Switch the RomM UI language — 19 locales supported in 5.0. --- # Languages -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +RomM's UI is translated into 19 locales. Pick yours from **Profile → User Interface → Language**. The change is immediate — no reload, no re-login. + +## Supported locales + +| Code | Language | +| --- | --- | +| `en_US` | English (United States) — default | +| `en_GB` | English (United Kingdom) | +| `es_ES` | Spanish (Spain) | +| `fr_FR` | French | +| `de_DE` | German | +| `it_IT` | Italian | +| `pt_BR` | Portuguese (Brazil) | +| `ja_JP` | Japanese | +| `ko_KR` | Korean | +| `ru_RU` | Russian | +| `pl_PL` | Polish | +| `cs_CZ` | Czech | +| `hu_HU` | Hungarian | +| `ro_RO` | Romanian | +| `bg_BG` | Bulgarian | +| `zh_CN` | Chinese (Simplified) | +| `zh_TW` | Chinese (Traditional) | + +Two more ship as work-in-progress with partial coverage — check the language dropdown to see the current list. + +## What gets translated + +- All UI strings: menus, buttons, labels, tooltips, dialogs. +- Error messages and notifications. +- Help text and inline instructions. + +Not translated: + +- **Game metadata** — titles, descriptions, genres come from metadata providers. English is usually the source language; IGDB has some localisation but coverage is uneven. +- **Your own content** — collection names, notes, user-uploaded media. +- **Docs site** (what you're reading) — English only in 5.0. + +## Missing strings + +If a translation is incomplete, you'll see the English fallback for untranslated strings. This is intentional — better than showing a broken UI in a half-translated locale. + +Spotted something missing? Help translate it — see below. + +## Contributing a translation + +### Add missing strings to an existing locale + +1. Fork [rommapp/romm](https://github.com/rommapp/romm). +2. Under `frontend/src/locales//`, open the JSON / YAML files. +3. Add the missing keys — they're all present in `en_US` for reference. +4. Open a PR. + +### Add a new locale + +1. Create `frontend/src/locales//` — copy the structure from `en_US`. +2. Translate. +3. Register the locale in the `frontend/src/locales/index.ts` file (the filename may change — follow the pattern in the repo). +4. Open a PR. + +Partial translations are welcome — we'd rather have a 70%-translated locale up for people to improve than hold out for 100%. + +See [Translations (i18n)](../developers/i18n.md) for the full contributor walkthrough. + +## Persistence + +Your language choice is stored server-side on your user record, so it follows you across devices. If you're not signed in (Kiosk mode, for example), the UI picks based on browser `Accept-Language` header and falls back to `en_US`. + +## Locale-specific notes + +### Right-to-left (RTL) + +No RTL locales in 5.0. Arabic and Hebrew translations may land in a future release; the UI needs some layout work before it can present them cleanly. + +### CJK typography + +CJK locales (Japanese, Korean, Chinese simplified/traditional) use the browser's default system font stack. Some game titles with rare glyphs may render inconsistently across OSes — that's a browser font issue, not a RomM one. + +## Docs i18n + +The docs site is currently English-only. Localising docs is a much bigger commitment than localising the app — if you'd like to help, open an issue on [rommapp/docs](https://github.com/rommapp/docs) and we can discuss scope. + +## Related + +- [User Interface settings](account-and-profile.md) — full list of personal UI preferences. +- [Console Mode](console-mode.md) — same locale selection applies. +- [Developers → Translations (i18n)](../developers/i18n.md) — contributor guide. diff --git a/docs/using/mobile-and-tv.md b/docs/using/mobile-and-tv.md index 187be756..6eece1d6 100644 --- a/docs/using/mobile-and-tv.md +++ b/docs/using/mobile-and-tv.md @@ -1,10 +1,115 @@ --- -status: placeholder -wave: 2 +title: Mobile & TV +description: Use RomM on phones, tablets, TVs, handhelds — recommended setups and companion apps. --- # Mobile & TV -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +RomM is a web app. Anything with a modern browser can use it — phones, tablets, handhelds, TVs, set-top boxes. This page walks through the recommended setup for each form factor. + +## Phones and tablets + +### Option A — the PWA + +Install RomM as a [Progressive Web App](pwa.md) — feels native, launches from the home screen, no app store. Recommended for most mobile users. + +- Good for: browsing, managing your library, downloading ROMs, in-browser play on devices with enough horsepower. +- Limited by: no push notifications (yet), no OS-level file pickers for ROM uploads (use the web UI instead), Safari quirks on iOS. + +### Option B — a native app + +[Community-maintained mobile apps](../ecosystem/community-apps.md) exist: + +- **Argosy Launcher** (Android) — official first-party. ROM syncing and launching. +- **romm-ios-app** / **romm-mobile** — community iOS and Android clients. + +Native apps give you proper file pickers, OS notifications, and nicer integration with device-level emulators (RetroArch mobile, Delta on iOS, etc.). + +### Browser choice + +- **Android** — Chrome, Firefox, or Samsung Internet. Chrome has best PWA support. +- **iOS** — Safari (the only way to install as PWA on iOS). + +### Touch vs mouse + +The main RomM UI handles touch gracefully. [Console Mode](console-mode.md) is gamepad-first but works with touch on handhelds that lack a gamepad. + +## TVs and set-top boxes + +You've got a TV-attached Android box, a mini-PC, a Nvidia Shield, or a browser running on the TV itself. The combo: + +### The setup + +1. **Install RomM as a PWA** on the device's browser. +2. **Set the default view to Console Mode** — Profile → User Interface → **Default view** → `/console`. +3. **Plug in a gamepad** — USB, Bluetooth, anything the browser sees. +4. **Launch the PWA.** You're in Console Mode, gamepad-ready. + +Now it looks and feels like a console UI, running on a web page. Launching a game loads EmulatorJS full-screen — same gamepad passes through. + +### What about actual emulation? + +Most TVs and cheap Android boxes don't have the horsepower for heavy in-browser emulation (Saturn, Dreamcast, PSP). Two workarounds: + +1. **Lighter cores** — EmulatorJS works fine on most platforms up to 16-bit and N64 on modest hardware. Stick to older systems and it's great. +2. **Native companion app** — install [RetroArch](https://retroarch.com/) or another emulator on the device, and sync ROMs/saves to it via a RomM companion app (Argosy, DeckRommSync, etc.). RomM's library management + your native emulator's playback. + +## Handhelds + +Handhelds running custom firmware (muOS, Batocera, Knulli, ArkOS, JELOS, ROCKNIX) aren't traditional RomM clients — they don't run browsers well, and you probably don't want to play in one anyway. The integration pattern is: + +1. **RomM hosts the library** on your home server. +2. **A companion app on the handheld** syncs ROMs, saves, and states to/from RomM. +3. **The handheld plays games locally** using its native emulators (RetroArch et al.). + +Recommended companions: + +- **[Grout](../ecosystem/grout.md)** — first-party. muOS and NextUI handhelds. +- **[DeckRommSync](../ecosystem/community-apps.md)** — Steam Deck (SteamOS). +- **[SwitchRomM](../ecosystem/community-apps.md)** — Nintendo Switch (homebrew). + +All of these use [Client API Tokens](../ecosystem/client-api-tokens.md) for auth and the [Device Sync Protocol](../ecosystem/device-sync-protocol.md) for the actual data transfer. + +## Steam Deck + +Two ways to use RomM on a Deck: + +### Desktop mode + +Any browser works. Use the PWA install flow (same as desktop) for a dedicated launcher. + +### Via DeckRommSync + +Syncs ROMs and saves to the Deck's local library so RetroArch / EmuDeck picks them up natively. Set up once, then play without RomM in the loop. See [Community Apps](../ecosystem/community-apps.md). + +Best combo: DeckRommSync for saves, plus the PWA for browsing / managing the library. + +## Bandwidth considerations + +Some things are bandwidth-hungry, some aren't: + +| Activity | Typical bandwidth | Notes | +| --- | --- | --- | +| Browsing the library | Low | Cover-art thumbnails; a few hundred KB per page load. | +| Playing in browser | Medium | ROM streams at boot; thereafter cached. PSP / Saturn ISOs can be hundreds of MB on first load. | +| Netplay | Medium–High | Video stream from host to players. ~500 kbps for SNES, more for higher-res cores. | +| Bulk download | As-much-as-you-want | Rate-limited only by your reverse proxy and network. | +| Device sync (saves) | Low | Saves are small; sync is fast. | +| Device sync (ROMs) | High | Pushing full ROM sets to a handheld initially is a lot. | + +On cellular? Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=false` (default — keep auth on) to avoid accidental discovery, and prefer native companion apps over in-browser play. + +## Self-hosting tips + +If you host RomM on a home server and want to reach it from cellular: + +- Put it behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). +- Use a VPN (Tailscale, WireGuard) instead of exposing to the internet. Handhelds with Tailscale setups "just work". +- For public access without a VPN, put Cloudflare Access or similar zero-trust auth in front of RomM. Disable `ALLOW_PUBLIC_REGISTRATION` on RomM — the edge auth handles gatekeeping. + +## See also + +- [Install as PWA](pwa.md). +- [Console Mode](console-mode.md). +- [Integrations & Ecosystem](../ecosystem/index.md) — every companion app RomM supports. +- [Community Apps](../ecosystem/community-apps.md) — the full list with platform / status flags. diff --git a/docs/using/pwa.md b/docs/using/pwa.md index 32059f6a..a71a646a 100644 --- a/docs/using/pwa.md +++ b/docs/using/pwa.md @@ -1,10 +1,114 @@ --- -status: placeholder -wave: 2 +title: Install as PWA +description: Add RomM to your phone or desktop home screen for an app-like experience. --- # Install as PWA -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +RomM ships as a **Progressive Web App** — a proper manifest, service worker, and icons. You can install it to your phone or desktop and launch it like a native app. Same RomM, no browser chrome. + +New in 5.0. + +## Why install? + +- **Looks and feels native** — icon on your home screen, fullscreen by default. +- **Faster startup** — the service worker caches the shell. +- **Mobile-friendly** — no browser address bar eating vertical space. +- **Offline-ish** — you can open the app without a network, though anything that actually fetches data still needs the server. + +## Install on Android + +### Chrome + +1. Open your RomM URL in Chrome. +2. Tap the **three-dot menu** → **Install app** (or "Add to Home screen" on older Chrome). +3. Confirm. + +An icon appears on your home screen. Launching opens RomM full-screen. + +### Samsung Internet / Other Android browsers + +Similar — look for "Add to Home screen" in the share / menu. + +## Install on iOS + +iOS Safari is slightly different and has no true PWA parity (some APIs are missing), but the basic install flow works: + +1. Open your RomM URL in **Safari** (not Chrome — on iOS, Chrome uses Safari's engine but doesn't expose the install flow). +2. Tap the **Share** button (square with arrow). +3. **Add to Home Screen**. +4. Name it, tap **Add**. + +The icon appears on your home screen. Launching opens RomM in a standalone Safari view. + +iOS limitations: + +- No push notifications. +- Service worker caching is more aggressive / unpredictable than on Android. +- The browser UI is fully gone — only gesture-based navigation. + +## Install on desktop + +### Chrome / Edge / Brave + +1. Open RomM. +2. Look for the **install icon** in the address bar (small computer with a down-arrow, on the right). +3. Click → **Install**. + +App launches in its own window, separate from the main browser. Pin to taskbar / dock as you would any other app. + +### Firefox + +Firefox desktop doesn't install PWAs out of the box. Workarounds: + +- Use a [Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/pwas-for-firefox/) like PWAsForFirefox. +- Or just use Chrome / Edge for the PWA. + +### Safari (macOS 14+) + +1. Open RomM in Safari. +2. **File** menu → **Add to Dock**. + +Same standalone window treatment as Chrome. + +## Uninstalling + +- **Android** — long-press the icon → **Uninstall** (or **Remove from Home screen**). +- **iOS** — long-press → **Remove App**. +- **Desktop** — inside the installed PWA window → three-dot menu → **Uninstall RomM**. + +Uninstalling just removes the shortcut and cached shell. Nothing server-side is touched. + +## Updating + +The service worker checks for new versions on launch. If RomM updates, the next time you open the PWA you get the latest shell. Browser-managed; no manual action. + +If a stale shell is causing issues (e.g. seeing old UI after an upgrade), force-refresh: + +- **Chrome/Edge mobile** — long-press the reload button → **Hard Reload**. +- **iOS Safari** — Settings → Safari → Clear History and Website Data → also removes the PWA cache. +- **Desktop PWAs** — the three-dot menu usually has a Reload option; hold Shift for a hard reload. + +## Limitations + +- **Requires HTTPS** — PWAs don't install from plain-HTTP hosts. Make sure your RomM is behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). +- **Icons** — RomM ships 192×192 and 512×512 manifest icons. Some devices pick a mid-size fallback that looks slightly blurry; known limitation, we'll expand the icon set over time. +- **No push notifications yet** — the PWA manifest doesn't register a notification handler in 5.0. Scan completion, task failures, etc. don't notify you. +- **Offline mode is partial** — opening the installed PWA offline shows the shell, but you can't actually browse the library or play anything without the server reachable. + +## Use with Console Mode + +PWA + [Console Mode](console-mode.md) is a powerful combo: + +- Install the PWA on a TV-attached Android box → set default view to `/console`. +- Launching the app goes straight to a gamepad-friendly library. Feels like a console. + +## Troubleshooting + +- **No Install option in Chrome** — RomM isn't on HTTPS, or the manifest is missing/broken. Open devtools → Application → Manifest to diagnose. +- **Icon shows as a generic globe** — manifest icons aren't loading. Check the image URLs in devtools → Application → Manifest. +- **App won't open offline** — expected; only the shell caches, not data. Network access is required for everything useful. + +## Want a native app instead? + +Community-made native apps are listed on [Ecosystem → Community Apps](../ecosystem/community-apps.md) — including Argosy Launcher, RommBrowser, and more. PWA is the zero-install path; native apps give you platform integration (notifications, deeper OS hooks) where it matters. diff --git a/docs/using/retroachievements.md b/docs/using/retroachievements.md index a69e4c2c..f25c9340 100644 --- a/docs/using/retroachievements.md +++ b/docs/using/retroachievements.md @@ -1,10 +1,103 @@ --- -status: placeholder -wave: 2 +title: RetroAchievements +description: Link your RetroAchievements account, surface progression, and browse unlocks on your games. --- # RetroAchievements -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +[RetroAchievements](https://retroachievements.org/) adds Xbox-style achievements to retro games. Thousands of classic titles have had achievement sets authored by the community — from NES through PSX and beyond. + +When RomM is wired up to RetroAchievements, each user's progression (achievements unlocked, percentage complete, hardcore flag) is surfaced per-game in the UI. + +## Prerequisites + +1. **RomM operator** has to enable the provider — see [Metadata Providers → RetroAchievements](../administration/metadata-providers.md#retroachievements). Check by looking for the achievement tab on any known-supported game; if it's absent, the provider isn't active. +2. **You** need a [RetroAchievements account](https://retroachievements.org/) and your personal **API key**. + +## Linking your account + +1. Go to your [RA settings page](https://retroachievements.org/settings). +2. Scroll to **Web API Keys** → copy the generated key. +3. In RomM → **Profile** → **RetroAchievements** section → paste: + - **Username**: your RA username (case-sensitive). + - **API Key**: the key you just copied. +4. **Sync now** — RomM pulls your progression data and populates the Achievements tab on matched games. + +From this point on, RomM auto-syncs every user's progression via the nightly RetroAchievements Sync scheduled task — see [Scheduled Tasks](../administration/scheduled-tasks.md). No manual sync needed, but you can force one from the Profile page whenever. + +## Where it shows up + +### Game detail → Achievements tab + +Per-game breakdown: + +- Total achievements authored. +- How many you've unlocked. +- Points earned / total points available. +- Hardcore vs softcore totals (if applicable). +- List of specific achievements with icons and descriptions. + +### Game cards + +A small badge appears on games where RA achievements exist, regardless of whether you've unlocked any yet. Hover for percentage complete. + +### Filters + +- **Show RetroAchievements** — filter to games that have RA achievements. +- You can combine with **Show Unmatched** or **Status: Playing** to find targets. + +### Smart collections + +[Smart collections](smart-collections.md) support a **Has Achievements** boolean field — build "Games with achievements I haven't started" with a rule combining this + your Personal status. + +## Hardcore mode + +RetroAchievements distinguishes two play modes: + +- **Softcore** — save states allowed. Achievements still count. +- **Hardcore** — no save states. More points per achievement; flagged separately. + +RomM doesn't enforce hardcore — you toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement, but marked as softcore. + +If you care about hardcore, use the in-game save feature instead of save states. See [Saves & States → RetroAchievements and states](saves-and-states.md#retroachievements-and-states). + +## Supported platforms + +Most 8/16-bit consoles and some later platforms: NES, SNES, Genesis, Game Boy family, PC Engine, Master System, Atari 2600/7800, N64, PlayStation, Saturn, Dreamcast, and more. Some platforms are hash-dependent — your ROM has to match the RA canonical dump. + +Full up-to-date list + hash requirements: [RA consoles + games](https://retroachievements.org/systemList.php). + +## Hash-based matching + +Whether a ROM has RA support depends on the **hash** matching RA's database. Hash calculation runs as part of scans — if you've disabled hashing (`filesystem.skip_hash_calculation: true`), you lose RA matching. + +Some tips: + +- Prefer **No-Intro** / canonical dumps. Hacks, region-patched ROMs, or unusual dumps won't match. +- If a game you know has RA support shows no achievements, the hash is probably off. Try a different ROM source, or re-run a Hashes scan in [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). + +## Privacy + +- Your RA API key is stored server-side (per-user), encrypted at rest. +- RomM only calls RA using your key for *your* data — never shares across users. +- Admins can see which users have RA linked but not the API keys themselves. + +To unlink: Profile → RetroAchievements → **Unlink** → confirm. Key is deleted; progression data stays cached until the next sync pass. + +## Troubleshooting + +- **"Invalid API key"** — regenerate on the RA settings page, paste fresh. +- **Games don't show the Achievements tab** — ROM hash doesn't match RA's canonical. Try a No-Intro dump. +- **Achievements show 0/N but you've unlocked some** — initial sync hasn't run yet. Click **Sync now** on your Profile page, or wait for the nightly. +- **Hardcore run didn't credit** — you probably loaded a save state mid-run. Start over and avoid states. + +More in [Troubleshooting](../troubleshooting/index.md). + +## API + +```http +GET /api/users/me/ra-progression # your progression data +POST /api/users/me/ra/refresh # trigger an on-demand sync +``` + +Requires `me.read` / `me.write`. Full API reference: [API Reference](../developers/api-reference.md). From 472bffd50f076b278a2a68a14b9c247e1ac210f5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 18:15:05 +0000 Subject: [PATCH 015/121] docs: complete Wave 2 platforms section (6 pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes out Wave 2 — the platforms/ narrative section now has all 6 pages drafted around the autogenerated supported-platforms table. - platforms/index.md: hub with quick-orientation routing - platforms/custom-platforms.md: expanded from 28-line stub. Lowercase-hyphenated folder naming, system.platforms remap for canonical platforms, custom-icon bind-mount recipe, what custom platforms don't get (metadata, EmulatorJS, RA) - platforms/ms-dos.md: ported from the existing 602-line page, distilled to the actual knowledge (3 game categories, manual commandline vs .conf autoload, DOOM worked example with full .conf collapsed, Dungeon Keeper retail+CD pattern, retail troubleshooting, GOG re-release caveat, dosemu/exodos notes) - platforms/emulatorjs-config.md: NEW operator-facing tuning reference. Debug mode, cache_limit, DOS-specific toggles, Netplay pointer, per-core settings with worked 2P SNES example, control mapping schema walkthrough, per-user vs operator precedence matrix, pre-built batocera/ES-DE config pointers - platforms/ruffle-config.md: NEW. flash/browser folder-name requirement, Flashpoint metadata integration, saves via .sol shared objects, mouse+keyboard-only input caveats, what's not in 5.0 yet - platforms/firmware-by-platform.md: NEW lookup table of firmware requirements per platform (PSX, Saturn, Sega CD, GBA, DS, Lynx, NGP, Amiga, ColecoVision, Intellivision, 3DO, PCE CD, Dreamcast, Atari 5200/7800). Bundling multi-file firmware as zip, file-name case sensitivity, verifying integrity via No-Intro/Redump Builds clean with --strict (after fixing one anchor typo). --- docs/platforms/custom-platforms.md | 104 ++++++++++++- docs/platforms/emulatorjs-config.md | 180 +++++++++++++++++++++- docs/platforms/firmware-by-platform.md | 86 ++++++++++- docs/platforms/index.md | 28 +++- docs/platforms/ms-dos.md | 203 ++++++++++++++++++++++++- docs/platforms/ruffle-config.md | 99 +++++++++++- 6 files changed, 670 insertions(+), 30 deletions(-) diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index 8ca08700..6f68fbf0 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -1,10 +1,104 @@ --- -status: placeholder -wave: 2 +title: Custom Platforms +description: Add platforms RomM doesn't natively support, plus custom platform icons. --- # Custom Platforms -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +RomM ships with support for ~400 platforms. If yours isn't in [the list](supported-platforms.md), you can still load it as a custom platform — RomM just won't have metadata-provider coverage for it. + +## Adding a custom platform + +Make a folder for the platform under your library root. Rules: + +- **All lowercase.** +- **Use `-` to separate words.** +- **No whitespace.** + +Examples: + +| Folder name | Displays as | +| --- | --- | +| `pocket-challenge-v2` | Pocket Challenge V2 | +| `my-homebrew-console` | My Homebrew Console | +| `wasm4` | Wasm4 | + +Then either run a **Quick Scan** (the platform is auto-discovered) or trigger a **New Platforms** scan from the **Scan** page. + +## Mapping to a canonical platform (preferred when possible) + +If your platform is one RomM supports but under a different slug, you don't need a custom platform — just **remap**. Add to `config.yml`: + +```yaml +system: + platforms: + super_nintendo: "snes" # your folder → canonical slug + psx: "ps" + game-cube: "ngc" +``` + +This gets you full metadata-provider support with your preferred folder name. See [Configuration File → `system.platforms`](../reference/configuration-file.md#systemplatforms). + +## Adding a custom platform icon + +If your platform isn't in [the built-in icons list](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms), RomM shows a default fallback icon. + +To load your own: + +### 1. Mount the platform icons directory + +Bind-mount a host directory onto the container's icon path: + +```yaml +services: + romm: + volumes: + - /path/to/your/icons:/var/www/html/assets/platforms +``` + +### 2. Seed it with the official icons + +RomM's built-in icons live at [`frontend/assets/platforms`](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms). Download them all and drop into your mounted directory — otherwise built-in platforms lose their icons too (because your mount overrides the whole directory). + +### 3. Add your custom `.ico` files + +The filename has to **match the IGDB platform slug**. Examples: + +| Platform | IGDB slug | Filename | +| --- | --- | --- | +| Amstrad CPC | `acpc` | `acpc.ico` | +| Pocket Challenge V2 | `pocket-challenge-v2` (custom) | `pocket-challenge-v2.ico` | +| NES | `nes` | `nes.ico` | + +Find the slug in the URL of the platform's IGDB page — e.g. [igdb.com/platforms/acpc](https://www.igdb.com/platforms/acpc) → slug is `acpc`. + +### 4. Restart RomM + +`docker compose up -d` picks up the new icon mount. + +### Replacing an existing platform's icon + +Same mechanic — just use a filename matching an existing platform's slug and your file overrides the built-in. + +## What custom platforms don't get + +- **Metadata provider coverage.** Providers are IGDB-slug-driven. A genuinely unknown platform won't have IGDB / ScreenScraper / MobyGames data. +- **EmulatorJS support.** The platform has to match a known EmulatorJS core; see [Supported Platforms → EmulatorJS column](supported-platforms.md). +- **RetroAchievements.** Hash-based, but restricted to RA-supported platforms. + +For the niche platform case, you'll likely rely on filename-only matching (no provider) and browsing the library like a straight file system. + +## Contributing a platform + +If you've added a custom platform that should be supported natively by RomM — e.g. a widely-used platform that's missing — open an issue on [rommapp/romm](https://github.com/rommapp/romm/issues) with: + +- Platform name. +- IGDB platform URL (if it exists on IGDB). +- Typical file extensions. +- Suggested canonical slug. + +The team adds platforms fairly aggressively if they see community demand. + +## Screenshot + +Platforms view with custom icons diff --git a/docs/platforms/emulatorjs-config.md b/docs/platforms/emulatorjs-config.md index 1994410f..4e59ee1b 100644 --- a/docs/platforms/emulatorjs-config.md +++ b/docs/platforms/emulatorjs-config.md @@ -1,10 +1,180 @@ --- -status: placeholder -wave: 2 +title: EmulatorJS Configuration +description: Operator-level tuning of the EmulatorJS player — per-core settings, controls, cache, Netplay. --- # EmulatorJS Configuration -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +End-user side of EmulatorJS lives in [In-Browser Play](../using/in-browser-play.md). This page is **operator-facing** — how to tune EmulatorJS via `config.yml` for every user on your instance. + +All settings below live under the `emulatorjs:` key in [`config.yml`](../reference/configuration-file.md#emulatorjs). + +## Enable / disable + +```yaml +environment: + - ENABLE_EMULATORJS=true # default +``` + +Set to `false` to disable EmulatorJS entirely — useful on the slim image or when running headless with companion apps only. The Play button is hidden; Ruffle still works independently. See [Image Variants](../install/image-variants.md). + +## Debug mode + +```yaml +emulatorjs: + debug: true +``` + +Logs every available option for each core to the browser console when a game launches. Turn on when tuning; turn off in production (it's verbose). + +## Cache limit + +```yaml +emulatorjs: + cache_limit: 52428800 # 50 MB per ROM; null = unlimited +``` + +Each ROM's cache (core files, BIOS, settings) is capped. `null` removes the cap — useful for large PSP / Saturn libraries on hosts with plenty of disk. + +## DOS-specific toggles + +```yaml +emulatorjs: + disable_batch_bootup: true + disable_auto_unload: true +``` + +- **`disable_batch_bootup`** — skips the `autorun.bat` step when `dosbox-pure` loads. Try this if DOS games hang on boot. See [MS-DOS](ms-dos.md). +- **`disable_auto_unload`** — keeps the emulator running when the user navigates away. Default off (browsers unload the emulator on page change). + +## Netplay + +```yaml +emulatorjs: + netplay: + enabled: true + ice_servers: + - urls: "stun:stun.l.google.com:19302" + - urls: "turn:openrelay.metered.ca:443" + username: "openrelayproject" + credential: "openrelayproject" +``` + +Full Netplay config in [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50) and user-side docs in [Netplay](../using/netplay.md). + +!!! warning "Nightly CDN caveat" + Enabling Netplay switches some assets to EmulatorJS's nightly CDN. Occasional hiccups are possible. If you don't need Netplay, leave it off. + +## Per-core settings + +Every core exposes different options. Set defaults your users get pre-filled: + +```yaml +emulatorjs: + settings: + # Apply to all cores + default: + fps: show + + # Per-core + snes9x: + snes9x_region: ntsc + snes9x_layer_1: true + snes9x_overclock: false + + parallel_n64: + vsync: disable + parallel-n64-gfxplugin: "auto" + + ppsspp: + ppsspp_internal_resolution: "2" # 2x + ppsspp_frame_skipping: "0" +``` + +Finding core names and their options: + +1. Turn on `debug: true` temporarily. +2. Load a game in that core. +3. Browser console → filter for "option". +4. Copy the keys you care about. + +Upstream reference: [EmulatorJS core options](https://emulatorjs.org/docs4devs/settings/). + +## Control mapping + +```yaml +emulatorjs: + controls: + snes9x: + 0: # player 1 + 0: # B button slot (index 0) + value: x # keyboard key "x" + value2: BUTTON_2 # controller button 2 + 1: # Y button slot + value: z + value2: BUTTON_3 + 1: # player 2 + 0: + value: / + value2: BUTTON_2 +``` + +The structure: + +- Top level = core name. +- Second level = player number (`0`, `1`, `2`, `3`). +- Third level = button slot (core-specific; 0 is usually the first face button). +- `value` = keyboard key. +- `value2` = gamepad button. + +Slot indexes and what they map to vary per core — see [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/). + +Users can override operator-level defaults in-game via Menu → **Controls**. Operator-level just sets the starting point. + +## Example: 2-player SNES setup + +```yaml +emulatorjs: + settings: + snes9x: + snes9x_region: ntsc + controls: + snes9x: + 0: # P1 on keyboard (WASD cluster) + 0: { value: ",", value2: "BUTTON_2" } # B + 1: { value: ".", value2: "BUTTON_3" } # A + 2: { value: "l", value2: "BUTTON_1" } # Y + 3: { value: "p", value2: "BUTTON_4" } # X + 1: # P2 on arrows + numpad + 0: { value: "/", value2: "BUTTON_2" } + 1: { value: "'", value2: "BUTTON_3" } +``` + +## Per-user vs operator-level + +| Where the setting is stored | Who it affects | Survives upgrades? | +| --- | --- | --- | +| Operator: `config.yml` / env vars | Everyone, as default | Yes | +| Per-user: in-game Menu → Settings | Just that user | Yes | +| Per-user: in-game Menu → Controls | Just that user | Yes | + +Per-user overrides take precedence. A config.yml setting is the fallback. + +## Pre-built config examples + +For frontend parity: + +- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml) — Batocera / RetroBat control + setting layouts. +- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) — ES-DE layout. + +## Troubleshooting + +- **Settings don't apply** — `debug: true`, reload a game, check console for "option set" logs. Core name might be wrong. +- **Netplay breaks after enabling** — see [Netplay Troubleshooting](../troubleshooting/netplay.md). +- **Control map works in one game, not another** — different cores; check you've set the right core name for the platform. + +## See also + +- [In-Browser Play](../using/in-browser-play.md) — end-user-facing. +- [Configuration File → `emulatorjs`](../reference/configuration-file.md#emulatorjs) — full schema. +- [EmulatorJS docs](https://emulatorjs.org/docs/) — upstream reference. diff --git a/docs/platforms/firmware-by-platform.md b/docs/platforms/firmware-by-platform.md index 439a9547..51881eea 100644 --- a/docs/platforms/firmware-by-platform.md +++ b/docs/platforms/firmware-by-platform.md @@ -1,10 +1,86 @@ --- -status: placeholder -wave: 2 +title: Firmware by Platform +description: Per-platform firmware/BIOS requirements for in-browser play. --- # Firmware by Platform -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +This page lists the firmware RomM's EmulatorJS cores need for each platform. For the admin-side workflow (uploading, managing, bundling multi-file firmware), see [Firmware Management](../administration/firmware-management.md). + +!!! note "Legality is your problem" + RomM does not ship firmware. Legality of obtaining firmware varies by jurisdiction and by whether you own the hardware. The project can't help you acquire BIOS files. + +## Platforms that need firmware + +| Platform | Folder slug | Required files | Notes | +| --- | --- | --- | --- | +| **PlayStation (PSX)** | `ps` | `scph1001.bin` (US), `scph5501.bin` (US), `scph5502.bin` (EU), `scph7502.bin` (JP) | At least one region BIOS required. | +| **Saturn** | `saturn` | `saturn_bios.bin`, optional `mpr-17933.bin` (EU) | Bundle both in a zip. | +| **Sega CD / Mega CD** | `segacd` | `bios_CD_U.bin` (US), `bios_CD_E.bin` (EU), `bios_CD_J.bin` (JP) | Region depends on your games. | +| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional — games run without it, but accurate emulation needs it. | +| **Nintendo DS** | `nds` | `bios7.bin`, `bios9.bin`, `firmware.bin` | All three are needed. | +| **Atari Lynx** | `lynx` | `lynxboot.img` | — | +| **Neo Geo Pocket / Color** | `ngp` / `ngpc` | `neopop.rom` | Bundle as zip. | +| **Amiga** | `amiga` | Kickstart ROMs (various) | `puae` core. Kickstart 1.2, 1.3, 3.1 depending on title. Bundle as zip. | +| **ColecoVision** | `colecovision` | `coleco.rom` | — | +| **Intellivision** | `intellivision` | `exec.bin`, `grom.bin` | Bundle both. | +| **3DO** | `3do` | `panafz10.bin` (or similar region BIOS) | — | +| **PC Engine CD / TurboGrafx-CD** | `pcecd` / `tg16cd` | `syscard3.pce` | — | +| **Dreamcast** | `dc` | `dc_boot.bin`, `dc_flash.bin` | EmulatorJS Dreamcast support is limited; results vary. | +| **Atari 5200** | `atari5200` | `5200.rom` | — | +| **Atari 7800** | `atari7800` | `7800 BIOS (U).rom` | — | + +For cores + platforms not in this table — most Nintendo consoles (NES, SNES, N64), Sega Genesis, Atari 2600, Game Boy, most home computers — **no firmware is needed**. + +## Bundling multi-file firmware + +Several platforms need multiple files (DS, Amiga, Saturn with region BIOS, Neo Geo Pocket). Pack all the files into a single `.zip` and upload it through the Firmware button on the platform view. EmulatorJS unpacks automatically. + +See [Firmware Management](../administration/firmware-management.md). + +## Where firmware goes on disk + +Depends on your [folder structure](../getting-started/folder-structure.md): + +- **Structure A**: `/romm/library/bios/{platform-slug}/` +- **Structure B**: `/romm/library/{platform-slug}/bios/` + +So PSX BIOS goes in `bios/ps/` (A) or `ps/bios/` (B). + +## File name requirements + +Emulator cores look for **specific filenames**. If you rename `scph1001.bin` to something else, PSX won't boot. + +Common mistakes: + +- Upper vs lower case — most cores are case-sensitive. Use exact case from the table above. +- Extensions — `.bin` vs `.rom` vs `.img` depend on the core. Don't rename. +- Version suffixes — `scph1001 (US) v4.4.bin` won't load; it has to be `scph1001.bin`. + +If you're not sure what name a core expects, check the [upstream EmulatorJS systems docs](https://emulatorjs.org/docs/systems/) or load the game with `emulatorjs.debug: true` — the browser console will tell you what file it tried to load. + +## Getting firmware + +Your options (jurisdiction-dependent): + +- **Dump from real hardware you own.** The cleanest answer. +- **Community archives.** Not linked here; the RomM project doesn't host these and doesn't recommend specific sources. +- **Official emulator bundles.** Some modern emulators ship with (or include a button to download) BIOS files. If you bought one, it's yours to use. + +## Verifying integrity + +Firmware is hash-verified by the emulator — a wrong hash means the file won't load. Quick CLI check: + +```sh +sha1sum scph1001.bin +# cbc7bad7bae8de7f6cfb49f1018ca55e8ab0c50a scph1001.bin +``` + +Known-good hashes for common BIOS files are documented on [No-Intro](https://datomatic.no-intro.org/) and [Redump](http://redump.org/). + +## See also + +- [Firmware Management](../administration/firmware-management.md) — admin-side upload and lifecycle. +- [Supported Platforms](supported-platforms.md) — catalogue with firmware-required flag. +- [In-Browser Play Troubleshooting → "Firmware required"](../troubleshooting/in-browser-play.md#firmware-required) — diagnostic. +- [EmulatorJS systems reference](https://emulatorjs.org/docs/systems/) — upstream firmware requirements per core. diff --git a/docs/platforms/index.md b/docs/platforms/index.md index 1d57dea0..6d08d7df 100644 --- a/docs/platforms/index.md +++ b/docs/platforms/index.md @@ -1,10 +1,28 @@ --- -status: placeholder -wave: 2 +title: Platforms & Players +description: Everything about the platforms RomM supports — catalogue, custom mappings, emulator tuning, firmware. --- # Platforms & Players -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +This section covers: + +- **[Supported Platforms](supported-platforms.md)** — the canonical catalogue of every platform RomM ships with. Platform slugs, active metadata providers per platform, EmulatorJS support flag, and firmware requirements. +- **[Custom Platforms](custom-platforms.md)** — how to add platforms RomM doesn't know about, plus custom platform icons. +- **[MS-DOS](ms-dos.md)** — long-form guide to DOS on RomM: `dosbox-pure`, installer ROMs, autorun scripts, known issues. +- **[EmulatorJS Configuration](emulatorjs-config.md)** — operator-level tuning of the EmulatorJS player: per-core settings, control mappings, cache limits, Netplay. +- **[Ruffle Configuration](ruffle-config.md)** — Flash / Shockwave configuration. +- **[Firmware by Platform](firmware-by-platform.md)** — which firmware files each platform needs, file names, and where to get them legally. + +## Quick orientation + +- **"What platforms does RomM support?"** → [Supported Platforms](supported-platforms.md). +- **"My platform isn't in the list."** → [Custom Platforms](custom-platforms.md) or map it via `system.platforms` in [`config.yml`](../reference/configuration-file.md#systemplatforms). +- **"I want to tweak how an emulator behaves."** → [EmulatorJS Configuration](emulatorjs-config.md). +- **"My game needs a BIOS."** → [Firmware by Platform](firmware-by-platform.md) + [Firmware Management](../administration/firmware-management.md). + +## Related sections + +- **[In-Browser Play](../using/in-browser-play.md)** — the end-user side of EmulatorJS + Ruffle. +- **[Metadata Providers](../administration/metadata-providers.md)** — which providers each platform has coverage from. +- **[Folder Structure](../getting-started/folder-structure.md)** — how the platform slug maps to on-disk folder names. diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md index 8c361ddb..133043a4 100644 --- a/docs/platforms/ms-dos.md +++ b/docs/platforms/ms-dos.md @@ -1,10 +1,203 @@ --- -status: placeholder -wave: 2 +title: MS-DOS +description: How to run DOS games in RomM via dosbox-pure — homebrew, shareware demos, and retail CDs. --- + + # MS-DOS -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-pure) — the EmulatorJS integration. Create a DOS platform (folder named `dos`) and drop your games in. + +!!! tip "Upload games as `.zip`" + `dosbox-pure` knows how to unzip and auto-mount zipped DOS games. Much easier than packaging a raw folder. + +!!! info "Save states work" + Once you've got a game running, save state and resume from there. You only need to do the boot dance once per game. + +## Game categories + +DOS games come in three flavours — each needs a different approach: + +- **Homebrew** — indie / modern DOS games. Just an `.exe`; usually Just Works once mounted. +- **Shareware demos** — what most "free DOS games" sites distribute. Same as homebrew — all files in one folder. +- **Retail** — need the original CD mounted alongside the installed game files. More work; every game is different. + +## The manual way (commandline) + +Select `Commandline` from the initial loading screen, then: + +```text +mount A / -t floppy # mount the game files +A: # switch to that drive +dir # find the .EXE +filename.exe # launch +``` + +Works for homebrew and shareware. Retail games usually fail here — they want a CD mounted somewhere. + +## The automatic way (`.conf` autoload) + +`dosbox-pure` looks for a `.conf` file matching the `.exe` name before booting. If found, it reads the config, mounts whatever's specified, and runs the `[autoexec]` block. + +Once you have a working `.conf`, click Play → game boots straight into DOS → `[autoexec]` runs → you're in the game. No typing. + +### Homebrew / demo example — DOOM shareware + +1. Confirm no `.ins`, `.cue`, or `.bin` files in the folder (those mean it's a retail CD game). +2. Create `DOOM.conf` next to `DOOM.exe`: + +
+DOOM.conf + +```ini +[sdl] +fullscreen=TRUE +fulldouble=false +fullresolution=Fixed +windowresolution=1280x800 +output=direct3d +autolock=true + +[dosbox] +machine=svga_s3 +memsize=16 + +[render] +frameskip=0 +aspect=false +scaler=normal3x + +[cpu] +core=auto +cputype=auto +cycles=max + +[mixer] +nosound=false +rate=22050 +blocksize=2048 + +[sblaster] +sbtype=sb16 +sbbase=220 +irq=7 +dma=1 +oplmode=auto + +[speaker] +pcspeaker=true + +[joystick] +joysticktype=auto + +[dos] +xms=true +ems=true +umb=true + +[autoexec] +@echo off +Mount C ".." +C: +cls +DOOM.exe +:exit +exit +``` + +
+ +Walkthrough of `[autoexec]`: + +- `Mount C ".."` — mount the current folder as drive `C:`. +- `C:` — switch to `C:`. +- `cls` — clear the screen. +- `DOOM.exe` — launch the game. +- `:exit` + `exit` — return after quitting. + +3. Zip up the folder (including `DOOM.conf`). +4. Upload to RomM under the `dos` platform. +5. Click Play. Game boots. + +Blank screen after boot? Something's off in `[autoexec]` — usually a path or mount issue. + +### Retail with CD example — Dungeon Keeper + +Retail games need the game CD image mounted alongside the install directory. + +1. Organise files: + ```text + DungeonKeeper/ + KEEPER.exe + KEEPER.cfg + ... + CD/ + DUNGEON.CUE + DUNGEON.BIN + ``` +2. Read `KEEPER.cfg` — it tells you where the game expects the CD. +3. Create `KEEPER.conf` with this `[autoexec]`: + + ```ini + [autoexec] + @echo off + Mount C ".." + C: + cd CD + imgmount d DUNGEO~8.CUE -t iso -fs iso + cd .. + cls + KEEPER.exe + :exit + exit + ``` + + Key lines: + - `Mount C ".."` — game files as `C:`. + - `imgmount d DUNGEO~8.CUE -t iso -fs iso` — CD image as `D:`. + - `cd ..` — back to `C:\` where `KEEPER.exe` lives. + - `KEEPER.exe` — run. + + (File names in DOS are 8.3 — `DUNGEON8.CUE` becomes `DUNGEO~8.CUE`.) + +4. Zip, upload, play. + +!!! info "GOG DOS re-releases don't work" + GOG's DOS games ship with custom wrappers (DOSBox Staging, ScummVM scripts, etc.) that don't translate to `dosbox-pure`. 100% failure rate observed. For GOG titles, extract the underlying game files and build a `.conf` yourself. + +### Retail troubleshooting + +If the game lands on a blank DOS prompt: + +1. **Remove the final `.exe` line from `[autoexec]`** — drops you at a prompt after mounting. Check manually: + - Is the game directory mounted correctly? + - `type DEFAULT.cfg` (or whatever the game's config is named) — the install path should line up with what you mounted. +2. **Check drive paths.** Many retail DOS games hard-code `D:\` as the CD. If you mount the CD as `E:`, the game won't find it. +3. **Debug with native `dosbox-pure`.** Run the same zip in [RetroArch](https://retroarch.com/) with the `dosbox-pure` core. If it works there, it should work in RomM. If it doesn't, the `.conf` is the problem. + +## Config file deep dive + +The DOOM example above is minimal. The full config can include dozens more options (sound card tweaks, CPU cycles, joysticks, IPX networking). The `dosbox-pure` wiki is the authoritative reference for every option. + +## Netplay on DOS + +Not supported — `dosbox-pure` works with EmulatorJS Netplay intermittently, not reliably. Stick to single-player for DOS. + +## Known issues + +- **Mouse lag** — enable `autolock=true` in `[sdl]`. +- **Wrong resolution / aspect** — tweak `[render] aspect=` and `windowresolution=`. +- **Audio crackle** — lower `rate=22050` to `rate=11025` or raise the mixer blocksize. +- **Game runs too fast** — most auto-cycle issues; set `cycles=fixed 4000` (or another number) explicitly. +- **Keyboard doesn't work for certain keys** — `usescancodes=false` sometimes fixes it. + +## Dosemu / Exodos alternatives + +Dosemu (Linux native) and Exodos (Windows frontend) are separate DOS tools, not integrated into RomM. If you have an Exodos installation you want to browse through RomM, add the Exodos `MS-DOS/eXoDOS/Games/` folder as a `dos` platform in RomM and RomM will see every game as a normal DOS ROM. + +## See also + +- [In-Browser Play](../using/in-browser-play.md) — general EmulatorJS behaviour. +- [EmulatorJS Configuration](emulatorjs-config.md) — operator-level tuning, including `disable_batch_bootup` for DOS-specific issues. +- [dosbox-pure docs](https://github.com/schellingb/dosbox-pure) — upstream reference for every config option. diff --git a/docs/platforms/ruffle-config.md b/docs/platforms/ruffle-config.md index f502191e..76abb22f 100644 --- a/docs/platforms/ruffle-config.md +++ b/docs/platforms/ruffle-config.md @@ -1,10 +1,99 @@ --- -status: placeholder -wave: 2 +title: Ruffle Configuration +description: Operator-level setup for the Ruffle Flash / Shockwave player. --- # Ruffle Configuration -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 2) and - has not been written yet. See the overhaul plan for status and ownership. +[Ruffle](https://ruffle.rs/) is RomM's Flash / Shockwave emulator. End-user details in [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle). This page is for operators. + +## Enable / disable + +```yaml +environment: + - ENABLE_RUFFLE=true # default +``` + +Set `false` to hide the Ruffle Play button globally. Useful on the slim image (Ruffle isn't bundled) or when you have no Flash content. + +## Platform folder names + +Ruffle **only** plays games from platform folders named `flash` or `browser`. No exceptions in 5.0. + +If your Flash games live under a differently-named folder: + +### Option 1 — rename + +Just rename the folder to `flash/` and rescan. + +### Option 2 — remap via `config.yml` + +```yaml +system: + platforms: + web-games: "flash" # your-folder: canonical-slug +``` + +Now `web-games/` is treated as the `flash` platform. See [Configuration File → `system.platforms`](../reference/configuration-file.md#systemplatforms). + +## Supported content + +- **Flash (SWF)** — 2D games, most work cleanly. AS1/AS2 excellent; AS3 still maturing in Ruffle. +- **Shockwave (DCR)** — partial support; complex 3D Shockwave games often fail. +- **FLV / F4V** — Flash video; playable but not game-like. + +[Ruffle compatibility database](https://ruffle.rs/#compatibility) has per-title status if you want to check a specific game. + +## File naming + +Ruffle honours the usual [RomM naming conventions](../getting-started/folder-structure.md#naming-convention) — filename tags, regions, revisions. + +No specific requirements beyond that — Ruffle reads the SWF directly. + +## Saves + +Flash's "shared objects" (`.sol` files, the Flash equivalent of cookies + saves) are persisted to `/romm/assets///saves/`. Per-user per-ROM. Appears on the game detail's **Game Data** tab like emulator saves. + +## Metadata + +Metadata coverage for Flash games comes from the [Flashpoint](../administration/metadata-providers.md#flashpoint) provider. Enable: + +```yaml +environment: + - FLASHPOINT_API_ENABLED=true +``` + +Then run an **Unmatched** scan on your `flash` platform. Titles, descriptions, cover art, tags — all populated if the game exists in Flashpoint's ~180,000-entry database. + +## Controls + +Flash was designed around mouse + keyboard. Ruffle passes input through natively: + +- **Mouse** — full support. +- **Keyboard** — full support. +- **Gamepad** — **not supported.** Flash games using XInput or similar don't work. + +On a handheld / Console Mode, Flash games are generally unplayable unless you've got a touchscreen + keyboard or a mouse. + +## Version / updates + +Ruffle is bundled in the full RomM image. Updates to Ruffle land when RomM updates its image — there's no separate Ruffle update knob. + +## Not in 5.0 yet + +- **Per-game config overrides** — Ruffle supports some game-specific options upstream, but RomM doesn't surface them yet. +- **Networking** — Flash games that hit remote servers typically fail (those servers are dead). No proxy / emulated backend for networked Flash games. +- **Control remapping** — straight passthrough only. + +## Troubleshooting + +- **Play button is missing on a Flash game** — platform folder isn't named `flash` or `browser` (or mapped via `system.platforms`). +- **Game loads but blank** — AS3 game Ruffle doesn't handle yet. Check [Ruffle compatibility](https://ruffle.rs/#compatibility). +- **Game says "Ruffle not loaded"** — you're on the slim image. Switch to full, or set `ENABLE_RUFFLE=false` to hide the Play button cleanly. + +See [In-Browser Play Troubleshooting → Ruffle](../troubleshooting/in-browser-play.md#ruffle-games). + +## See also + +- [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle) — end-user side. +- [Metadata Providers → Flashpoint](../administration/metadata-providers.md#flashpoint) — where Flash metadata comes from. From 59f584a530f7fd5b9f19203476ded3d32c345e87 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 18:21:14 +0000 Subject: [PATCH 016/121] docs: start Wave 3 ecosystem (7 core pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First ecosystem batch — hub + four first-party apps + two developer-facing protocol pages + community apps index. - ecosystem/index.md: hub with first-party / feeds / community / build-your-own groupings - ecosystem/argosy.md: NEW. Android launcher first-party docs. APK install, pairing flow, emulator config, save sync modes, permissions, troubleshooting - ecosystem/grout.md: NEW. muOS / NextUI handheld companion. .muxapp install, pairing, sync cadence, SSH mode pointer - ecosystem/playnite-plugin.md: ported from Integrations/. Four install paths, emulator-mapping table with examples, Client API Token note for safer auth than password-in-plaintext - ecosystem/client-api-tokens.md: NEW and critical for ecosystem devs. Token format, creating, pairing-code flow with ASCII sequence diagram, full API reference for implementers (exchange, generate, poll-status, lifecycle), scoping recipes per app type, role-change behaviour, anti-patterns - ecosystem/device-sync-protocol.md: NEW. Wire-level reference. Devices / sync sessions / play sessions primitives, registration, sync negotiation with full request/response examples, operation types (upload/download/conflict/noop), session completion, standalone play-sessions ingest, SSH alternative transport - ecosystem/community-apps.md: NEW index. Mobile / desktop / handheld / services groupings. Per-app: author, platform, status, repo link. Clear "community-maintained" disclaimer. PR submission + abandoned-app reporting flow Builds clean with --strict. --- docs/ecosystem/argosy.md | 96 +++++++++- docs/ecosystem/client-api-tokens.md | 179 +++++++++++++++++- docs/ecosystem/community-apps.md | 134 +++++++++++++- docs/ecosystem/device-sync-protocol.md | 246 ++++++++++++++++++++++++- docs/ecosystem/grout.md | 110 ++++++++++- docs/ecosystem/index.md | 71 ++++++- docs/ecosystem/playnite-plugin.md | 115 +++++++++++- 7 files changed, 916 insertions(+), 35 deletions(-) diff --git a/docs/ecosystem/argosy.md b/docs/ecosystem/argosy.md index c8fa5f93..195fa1b8 100644 --- a/docs/ecosystem/argosy.md +++ b/docs/ecosystem/argosy.md @@ -1,10 +1,96 @@ --- -status: placeholder -wave: 3 +title: Argosy Launcher +description: Official Android launcher for RomM — browse and launch your library on mobile. --- # Argosy Launcher -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +**Argosy Launcher** is RomM's first-party Android app. Browse your library, download ROMs on the fly, launch into RetroArch or your emulator of choice. + +- **Repo:** [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher) +- **Language:** Kotlin +- **License:** GPL-3.0 +- **Platforms:** Android + +## What it does + +- Browses your full RomM library from an Android device. +- Handles authentication via [Client API Tokens](client-api-tokens.md) — paired over a short code so you don't type a 44-character string. +- Downloads ROMs on demand to your device's storage. +- Launches RetroArch (or another configured emulator) with the downloaded ROM. +- Syncs saves / states back to RomM when you finish a session (optional). + +## Installing + +### From a release + +1. Grab the latest `.apk` from [GitHub Releases](https://github.com/rommapp/argosy-launcher/releases/latest). +2. Install via your Android device's package installer (you may need to enable "Install from unknown sources" in Settings). + +### Via F-Droid / Play Store + +Not currently on either. APK sideloading is the path for now. + +## First-time setup + +1. Launch Argosy. +2. Enter your RomM URL (e.g. `https://romm.example.com`). Needs HTTPS in production. +3. Choose **Pair Device**. +4. Argosy shows a pairing URL or QR code — open it on a device that's already signed into RomM. +5. On that device: RomM shows a confirmation dialog. Enter the 8-digit code Argosy displayed. +6. Accept — Argosy receives a Client API Token bound to your RomM account. + +From here, Argosy lists your library. Full pairing-flow details in [Client API Tokens](client-api-tokens.md). + +## Using Argosy + +### Browse + +The main view mirrors RomM's platform-and-collection layout. Tap a platform to see its games. Search works the same as the RomM web UI. + +### Download and launch + +Tap a game → **Download**. Argosy pulls the ROM to device storage. Once downloaded, **Play** launches it with your configured emulator. + +### Emulator configuration + +Argosy → Settings → Emulators. For each platform: + +- Pick the external emulator app (RetroArch, Duckstation, PPSSPP, etc.). +- Optionally pass core-specific args. + +RetroArch users: Argosy can auto-detect installed RetroArch cores and map platforms to them. + +### Save sync + +Argosy → Settings → Sync. Two modes: + +- **On session end** — uploads saves back to RomM when you exit the emulator. +- **Manual** — you tap Upload when you want. + +Saves show up in RomM's **Game Data** tab on the game, same as in-browser saves. See [Saves & States](../using/saves-and-states.md). + +## Permissions + +Argosy needs: + +- **Storage** — to save downloaded ROMs. +- **Network** — to talk to your RomM instance. +- **Optional: Notifications** — download-complete and sync-complete pings. + +No other permissions. The app doesn't request contacts, camera, location, or anything else. + +## Troubleshooting + +- **Can't connect to RomM** — check the URL (including `https://`), and that the RomM instance is reachable from your mobile network. Cellular might be blocked; try Wi-Fi first. +- **Token invalid** — pair again. Tokens can expire or be revoked on the RomM side. +- **Emulator won't launch** — make sure the emulator app is installed and Argosy has permission to open it. Some emulators require an intent-filter setup. +- **Downloads fail partway** — usually network; Argosy resumes on retry. + +Full sync-specific debugging in [Device Sync Troubleshooting](../troubleshooting/sync.md). + +## See also + +- [Client API Tokens](client-api-tokens.md) — the auth + pairing flow Argosy uses. +- [Device Sync Protocol](device-sync-protocol.md) — how saves sync. +- [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher) — source, issues, releases. diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index 6cd34893..66da4b43 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -1,10 +1,179 @@ --- -status: placeholder -wave: 3 +title: Client API Tokens +description: Long-lived bearer tokens for companion apps, plus the device-pairing flow. --- # Client API Tokens -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +A **Client API Token** is a long-lived credential that a companion app (or script, or CI job) uses to authenticate against RomM on behalf of a specific user. Think "personal access token" on GitHub. + +Tokens are **per-user** and **per-scope-subset** — a token can hold any subset of the owning user's scopes, scoped narrower than the user's role. You get up to **25 active tokens per user**. + +## Why not just store a password? + +- Passwords grant full access to the account; tokens can be scope-narrowed. +- Tokens are one-click revocable without changing your password. +- Tokens are safer to type (or paste) into a companion app's config file than a password. +- Tokens can be bound to a single device via the pairing flow, avoiding typing them at all. + +## Token format + +40 hex characters prefixed with `rmm_`: + +```text +rmm_abcdef0123456789abcdef0123456789abcdef01 +``` + +Send as a bearer token on any authenticated API call: + +```http +Authorization: Bearer rmm_abcdef... +``` + +## Creating a token + +From the RomM UI: **Profile → Client API Tokens → + New Token**. + +- **Name** — descriptive (e.g. "Grout on RG35XX"). +- **Scopes** — tick which scopes to include. Default: read-only. Think about it; don't give every token `users.write`. +- **Expiry** — optional; blank = never expires until revoked. + +The token is shown **exactly once**. Copy it now. If you lose it, revoke and regenerate — you can't get it back. + +## Device pairing (short-code flow) + +Typing a 44-character token into a handheld thumbstick isn't realistic. Instead: + +### Flow + +``` +┌───────────┐ ┌───────────┐ +│ Device │ │ RomM │ +└─────┬─────┘ └─────┬─────┘ + │ │ + │ 1. POST /api/client-tokens/{id}/pair │ + │ (from the web UI, by the token's owner) │ + │<── generates short code (8 digits) ─────────│ + │ │ + │ 2. Device user types the 8-digit code │ + │ into the companion app │ + │ │ + │ 3. Device: POST /api/client-tokens/exchange │ + │ body: { "code": "12345678" } │ + │──────────────────────────────────────────→ │ + │ │ + │<── full token (rmm_...) ────────────────────│ + │ │ + │ 4. Device stores the token, uses it from │ + │ now on. │ + │ │ +``` + +### Timing + +- Pairing codes are valid for **5 minutes** after creation. +- Single-use — once a device exchanges the code, it's invalid for anyone else. +- Re-create if the user doesn't complete within the window. + +### Who generates the code + +The user who owns the token, from a device already signed into RomM (web UI, usually). The handheld / companion device then enters the code. No way for a device to generate a code on its own — that would defeat the pairing. + +### What "pairing" gives you + +A Client API Token bound to the device's installation. The companion app stores the token and uses it on every subsequent API call. From RomM's side, it looks like any other token — no special treatment beyond the fact that pairing is how the token got there. + +## API reference for implementers + +All the endpoints a companion app needs: + +### Claim an outstanding pairing code (device-side) + +```http +POST /api/client-tokens/exchange +Content-Type: application/json + +{ "code": "12345678" } +``` + +Response: + +```json +{ + "id": 42, + "name": "Grout on RG35XX", + "token": "rmm_abcdef0123456789abcdef0123456789abcdef01", + "scopes": ["roms.read", "platforms.read", "assets.read", "assets.write"], + "expires_at": null +} +``` + +If the code is invalid / expired / already used, returns 401. + +### Generate a pairing code (server-side, called from the UI) + +```http +POST /api/client-tokens/{id}/pair +Authorization: Bearer +``` + +Requires the caller own the token. Response includes the short code. + +### Check pairing status (device-side, polling) + +```http +GET /api/client-tokens/pair/{code}/status +``` + +Returns whether the code has been claimed, expired, or is still waiting. Poll every few seconds from the device side if you want instant feedback. + +### Token lifecycle + +```http +POST /api/client-tokens # create (from UI) +GET /api/client-tokens # list own tokens +DELETE /api/client-tokens/{id} # revoke +PUT /api/client-tokens/{id}/regenerate # new secret, same scopes +``` + +Admin-only: + +```http +GET /api/client-tokens/all # list every token on the server +DELETE /api/client-tokens/{id}/admin # revoke any user's token +``` + +## Scoping tokens properly + +A token can only hold scopes the owning user *also* holds. If the user is an Editor, their token can hold any Editor scope, but not `users.write` (which is Admin-only). + +Useful narrow scope-sets: + +- **Library-only read**: `roms.read`, `platforms.read`, `collections.read`, `devices.read` — e.g. for a browse-only app. +- **Read + sync saves**: add `assets.read`, `assets.write`, `me.read`, `me.write`, `devices.write`. +- **Playnite / external launcher**: `roms.read`, `platforms.read`, `collections.read` — it only needs to browse and download. +- **Grout / handheld companion**: library-read + assets read/write + device management. + +The UI's scope-selection step defaults to read-only. Only tick write scopes you actually need. + +## What happens on user role change + +If the owning user's role drops below what the token needs: + +- Token continues to exist but fails at request time with **403 Forbidden**. +- RomM doesn't automatically revoke it — that's the user's decision. + +If the user is deleted, all their tokens are revoked immediately. + +## Anti-patterns + +- **Sharing a token between users.** Tokens are single-user. If two people need access, give them each an account and each creates their own token. +- **Embedding a token in public source.** Obvious but worth saying. If you accidentally commit one, revoke immediately from the RomM UI. +- **A single token for every app.** Name and scope per-app; revoking one doesn't kill the others. +- **Infinite-expiry tokens in untrusted locations.** If a device might be lost / handed off, set an expiry. + +## See also + +- [Device Sync Protocol](device-sync-protocol.md) — how the synced content flows after pairing. +- [API Authentication](../developers/api-authentication.md) — all RomM auth modes side-by-side. +- [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix) — the 19-scope taxonomy. diff --git a/docs/ecosystem/community-apps.md b/docs/ecosystem/community-apps.md index 9c7b29e3..dd2c795f 100644 --- a/docs/ecosystem/community-apps.md +++ b/docs/ecosystem/community-apps.md @@ -1,10 +1,134 @@ --- -status: placeholder -wave: 3 +title: Community Apps +description: Third-party companion apps for RomM — maintained by the community, not the RomM team. --- # Community Apps -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +Apps listed here are **community-maintained**. The RomM team doesn't build or officially support them — the authors do. Support is via the individual app's issue tracker and the RomM Discord. + +First-party alternatives (built by the RomM team): + +- [Argosy Launcher](argosy.md) (Android). +- [Grout](grout.md) (Linux handhelds). +- [Playnite Plugin](playnite-plugin.md) (Windows). +- [muOS App](muos-app.md) (muOS handhelds). + +## Mobile + +### romm-ios-app + +Native iOS client for RomM. + +- **Author:** [@ilyas-hallak](https://github.com/ilyas-hallak) +- **Platform:** iOS +- **Status:** Active + +### romm-mobile + +Android + iOS client. + +- **Author:** [@mattsays](https://github.com/mattsays) +- **Platform:** Android (iOS in progress) +- **Status:** Active + +## Desktop + +### RommBrowser + +Electron-based desktop browser for RomM. + +- **Author:** [@smurflabs](https://github.com/smurflabs) +- **Platform:** Windows, macOS, Linux +- **Status:** Active + +### RomMate + +Desktop app for browsing. + +- **Author:** [@brenoprata10](https://github.com/brenoprata10) +- **Platform:** Windows, macOS, Linux +- **Status:** Active + +### romm-client + +Another desktop client. + +- **Author:** [@chaun14](https://github.com/chaun14) +- **Platform:** Desktop +- **Status:** Active + +### RetroArch Sync + +Sync your RetroArch library with RomM. + +- **Author:** [@Covin90](https://github.com/Covin90) +- **Platform:** Anywhere RetroArch runs +- **Status:** Active + +## Handhelds + +### DeckRommSync + +SteamOS downloader/syncer for Steam Deck. + +- **Author:** [@PeriBluGaming](https://github.com/PeriBluGaming) +- **Platform:** Steam Deck (SteamOS) +- **Status:** Active + +### SwitchRomM + +Homebrew NRO app for Nintendo Switch — pull ROMs from RomM over Wi-Fi. + +- **Author:** [@Shalasere](https://github.com/Shalasere) +- **Platform:** Nintendo Switch (homebrew) +- **Status:** Active + +## Services + +### romm-comm + +Discord bot for interacting with RomM from a Discord server — query library, post stats, request games. + +- **Author:** [@idio-sync](https://github.com/idio-sync) +- **Platform:** Discord bot +- **Status:** Active + +### GGRequestz + +Game discovery and request tracker that integrates with RomM. + +- **Author:** [@XTREEMMAK](https://github.com/XTREEMMAK) +- **Platform:** Web service +- **Status:** Active + +### Syncthing Sync + +Push a Syncthing-managed library to RomM automatically. + +- **Author:** [@amn-96](https://github.com/amn-96) +- **Platform:** Wherever Syncthing runs +- **Status:** Active + +## "Community-maintained" — what it means + +- **The RomM team doesn't build these.** We won't fix bugs, ship features, or respond to support tickets for community apps. +- **Support through the app author.** Each project has its own issue tracker — use it. +- **Install at your own risk.** We don't code-review community apps or vouch for their security posture. +- **Token safety.** These apps use [Client API Tokens](client-api-tokens.md) the same way first-party apps do. Scope tokens narrowly and revoke if an app misbehaves. + +## Submitting a new app + +Built something? Open a PR on [rommapp/docs](https://github.com/rommapp/docs) adding your project to this list with: + +- Name, short description. +- Author GitHub handle. +- Platform(s). +- Repo link. +- Status (Active / Maintenance-mode / Abandoned). + +We'll merge if the project's real, has a working repo, and isn't obviously broken. + +## Reporting an abandoned app + +If an app here hasn't been updated in >12 months and the maintainer isn't responsive, let us know — either a PR marking it as Abandoned, or a Discord message in `#community-projects`. We flag abandoned projects so users know not to rely on them. diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md index 00720519..555d4ab2 100644 --- a/docs/ecosystem/device-sync-protocol.md +++ b/docs/ecosystem/device-sync-protocol.md @@ -1,10 +1,246 @@ --- -status: placeholder -wave: 3 +title: Device Sync Protocol +description: Wire-level reference for RomM's save/state/play-session sync protocol — for companion-app developers. --- # Device Sync Protocol -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +This page documents the **wire-level protocol** RomM uses for bidirectional sync between itself and companion apps. If you're building a companion app, this is your reference. + +End users: this is under the hood. You don't need to read it. See [Saves & States](../using/saves-and-states.md) and [SSH Sync](../administration/ssh-sync.md) for operator/user views. + +## The three primitives + +1. **Devices** — registered endpoints. Each device has a name, hostname, and links to a specific user's [Client API Token](client-api-tokens.md). +2. **Sync sessions** — an atomic bidirectional sync run. One session handles "pull anything new from server" + "push anything new from device" + "reconcile conflicts" + "ingest play sessions". +3. **Play sessions** — per-session records of ROM playtime. Independent of sync but commonly posted in batch at the end of a sync run. + +## Authentication + +Every API call requires a valid token: + +```http +Authorization: Bearer rmm_... +``` + +Required scopes: + +| Endpoint family | Scope | +| --- | --- | +| `/devices/*` | `devices.read`, `devices.write` | +| `/sync/*` | `assets.read`, `assets.write`, `devices.write` | +| `/play-sessions/*` | `me.read`, `me.write` (read own); `users.read` (read others') | +| `/assets/*` (save/state I/O) | `assets.read`, `assets.write` | + +## Registering a device + +After [pairing](client-api-tokens.md#device-pairing-short-code-flow), the device registers itself: + +```http +POST /api/devices +Authorization: Bearer +Content-Type: application/json + +{ + "name": "RG35XX - Living Room", + "platform": "muos", + "hostname": "rg35xx-livingroom.local", + "mac": "aa:bb:cc:dd:ee:ff", + "sync_mode": "push_pull", + "paths": { + "roms": "/roms", + "saves": "/saves", + "states": "/saves/states" + } +} +``` + +Response: + +```json +{ + "id": 17, + "name": "RG35XX - Living Room", + ... +} +``` + +The device remembers `id` for all subsequent calls. + +### Sync modes + +| Mode | Behaviour | +| --- | --- | +| `pull_only` | Server only pushes; device-side changes are ignored. | +| `push_only` | Device pushes saves up; server changes never flow down. | +| `push_pull` | Bidirectional (most common). | + +## Sync negotiation + +The core of the protocol. A device asks RomM: "here's what I have, tell me what to do." + +### Request + +```http +POST /api/sync/negotiate +Authorization: Bearer +Content-Type: application/json + +{ + "device_id": 17, + "roms": [ + { + "rom_id": 1234, + "saves": [ + { "file": "mario.srm", "mtime": "2026-04-18T09:42:01Z", "sha1": "abc..." }, + { "file": "mario.state", "mtime": "2026-04-18T09:45:00Z", "sha1": "def..." } + ] + }, + ... + ] +} +``` + +The device reports every save/state it knows about for each ROM — filename, last-modified time, and hash. + +### Response + +RomM returns a set of **operations** the device should execute: + +```json +{ + "session_id": "550e8400-e29b-41d4-a716-446655440000", + "operations": [ + { + "type": "upload", + "rom_id": 1234, + "file": "mario.srm", + "destination": "/api/saves", + "reason": "server_newer" + }, + { + "type": "download", + "rom_id": 5678, + "file": "zelda.srm", + "source": "/api/saves/42/content", + "dest_path": "/saves/zelda.srm" + }, + { + "type": "conflict", + "rom_id": 9999, + "file": "tetris.srm", + "server_mtime": "...", + "device_mtime": "...", + "resolution": "keep_both" + }, + { + "type": "noop", + "rom_id": 1111, + "file": "goldeneye.srm", + "reason": "already_synced" + } + ] +} +``` + +### Operation types + +- **`upload`** — device pushes the file to the server. +- **`download`** — device pulls the file from the server. +- **`conflict`** — both sides have newer versions. Resolution strategy: + - `keep_both` — rename and keep both copies. + - `server_wins` / `device_wins` — overwrite the other. + - Default is `keep_both`. +- **`noop`** — nothing to do; hashes match. + +### Execution + +For each operation the device runs the appropriate HTTP call. For an `upload`: + +```http +POST /api/saves +Authorization: Bearer +Content-Type: multipart/form-data + +[file upload] +``` + +For a `download`: + +```http +GET /api/saves/{id}/content +Authorization: Bearer +``` + +## Completing a sync session + +Once the device has executed every operation: + +```http +POST /api/sync/sessions/{session_id}/complete +Authorization: Bearer +Content-Type: application/json + +{ + "operations_completed": 15, + "operations_failed": 1, + "play_sessions": [ + { + "rom_id": 1234, + "start": "2026-04-18T09:00:00Z", + "end": "2026-04-18T09:45:00Z", + "duration_seconds": 2700 + }, + ... + ] +} +``` + +This closes out the sync session and ingests any play sessions from the device in one call. + +## Play sessions (standalone) + +If you just want to report a play session without running a full sync: + +```http +POST /api/play-sessions +Authorization: Bearer +Content-Type: application/json + +[ + { + "rom_id": 1234, + "start": "2026-04-18T09:00:00Z", + "end": "2026-04-18T09:45:00Z", + "device_id": 17 + } +] +``` + +Send up to 100 per request. + +## SSH-based sync (alternative) + +For handhelds where the RomM server connects over SSH instead of the device polling HTTPS, see [SSH Sync](../administration/ssh-sync.md). The RomM-side push-pull task reads the device's filesystem over SSH rather than going through API calls. Same data, different transport. + +Companion apps generally prefer the API model. SSH-based sync exists mostly for cases where the device can't run a TLS client reliably. + +## Rate limits + +Not strict in 5.0. Reasonable rule of thumb: + +- Sync once per session (not every save). +- Large bursts (initial sync of a full library) are fine — RomM handles them. +- Don't poll `/api/sync/negotiate` in a tight loop; it's expensive server-side. + +## Event notifications + +Currently polling-only. Companion apps check `/api/sync/negotiate` periodically (Grout defaults to every 15 minutes on Wi-Fi). Future versions may add a push notification channel; until then, polling. + +## See also + +- [Client API Tokens](client-api-tokens.md) — auth + pairing. +- [API Authentication](../developers/api-authentication.md) — general auth primer. +- [API Reference](../developers/api-reference.md) — full endpoint catalogue. +- [SSH Sync](../administration/ssh-sync.md) — alternative transport for handhelds. +- [Argosy](argosy.md), [Grout](grout.md) — reference client implementations. diff --git a/docs/ecosystem/grout.md b/docs/ecosystem/grout.md index dec13f32..3109a34d 100644 --- a/docs/ecosystem/grout.md +++ b/docs/ecosystem/grout.md @@ -1,10 +1,110 @@ --- -status: placeholder -wave: 3 +title: Grout +description: Official Linux handheld companion for muOS and NextUI — sync ROMs and saves from RomM. --- # Grout -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +**Grout** is RomM's first-party companion for Linux-based handhelds — specifically [muOS](https://muos.dev) and [NextUI](https://github.com/mattsays/nextui). Syncs ROMs, saves, and states bidirectionally with your RomM instance over Wi-Fi. + +- **Repo:** [rommapp/grout](https://github.com/rommapp/grout) +- **Language:** Go +- **License:** MIT +- **Platforms:** muOS (Anbernic and similar ARM handhelds), NextUI + +## What it does + +- Connects to your RomM instance using a [Client API Token](client-api-tokens.md). +- **Pulls** ROMs from RomM to the handheld's SD card, organised into muOS / NextUI's expected folder layout. +- **Pushes** saves and states back to RomM when you finish a session. +- **Schedules** sync runs — on idle, on session end, or on a cron. +- Works fully offline between syncs; the handheld doesn't need RomM to play. + +## Why Grout (and not Argosy)? + +- **[Argosy](argosy.md)** is Android-native. Good for Android handhelds (Retroid Pocket running Android, phones). +- **Grout** is for non-Android Linux handhelds — muOS and NextUI are Linux, not Android. Argosy's APK won't install. + +Same underlying protocol; different client for different OS. + +## Installing + +### On muOS + +1. Grab the latest `.muxapp` release from [GitHub Releases](https://github.com/rommapp/grout/releases/latest). +2. Copy to `/mnt/mmc/ARCHIVE/` on the device (USB, SD swap, or SSH). +3. Launch **Archive Manager** from the Applications menu. +4. Select the Grout `.muxapp` → install. +5. After install, find **Grout** under Applications. + +### On NextUI + +1. Download the NextUI-flavoured release from the same releases page. +2. Follow NextUI's standard app install flow (paths differ by device; see NextUI docs). + +## First-time setup + +1. Launch Grout on the handheld. +2. Enter your RomM URL. +3. **Pair Device** — Grout displays an 8-digit code. +4. On any device already signed into RomM, navigate to **Profile → Client API Tokens → + New Token** → create a new token → **Pair Device** → enter the code shown on Grout. +5. Grout receives the token, confirms pairing, and fetches your library metadata. + +Details on the pairing flow in [Client API Tokens](client-api-tokens.md). + +## Using Grout + +### Browse + +Grout's main view lists platforms. Select one to see available ROMs. Metadata (titles, cover art) is pulled live from RomM. + +### Download ROMs + +Select a ROM → **Download**. Grout pulls the file to the expected location on the SD card, so muOS / NextUI's native launcher sees it on next refresh. + +Bulk select for multi-file downloads — useful for pulling a whole collection at once. + +### Sync cadence + +Grout → Settings → Sync: + +- **Pull** cadence — how often to check RomM for new ROMs (default: manual; can set to every N minutes when on Wi-Fi). +- **Push** cadence — how often to upload saves (default: on session end). +- **Full sync** — manual; triggers a full bidirectional sync right now. + +### What pushes back to RomM + +- **Save files** — once a session ends, Grout uploads any changed saves to RomM. +- **Save states** — same, if enabled in settings. +- **Play session records** — start/end times for the **Continue Playing** ribbon and per-ROM playtime on RomM. + +## Permissions and access + +Grout uses your Client API Token for all API calls. Token scopes: + +- `roms.read`, `platforms.read`, `collections.read` — to browse. +- `assets.read`, `assets.write` — to sync saves. +- `devices.read`, `devices.write` — to register as a device. +- `firmware.read` — if you're syncing firmware too. + +Scope the token narrowly when creating — default scopes are fine for most users, but an admin shouldn't hand Grout `users.write` just because the token-creation page offers it. + +## SSH sync (operator-side) + +If you want Grout to pull from RomM over SSH rather than HTTPS — e.g. on a trusted LAN with no reverse proxy — see [SSH Sync](../administration/ssh-sync.md) for the server-side config. Grout supports both modes, selectable in Settings → Connection. + +## Troubleshooting + +- **Can't see the handheld's Wi-Fi on RomM's network** — make sure both are on the same SSID / VLAN. +- **Token invalid** — re-pair; token was revoked or expired. +- **Saves aren't syncing** — check the sync cadence is set to something other than "never", and that the handheld actually has network during the sync window. +- **"Device not registered"** — the pairing step wasn't completed. Re-run pairing from scratch. + +More in [Device Sync Troubleshooting](../troubleshooting/sync.md). + +## See also + +- [Client API Tokens](client-api-tokens.md) — token and pairing flow reference. +- [Device Sync Protocol](device-sync-protocol.md) — wire-level protocol. +- [SSH Sync](../administration/ssh-sync.md) — operator-side SSH config. +- [rommapp/grout](https://github.com/rommapp/grout) — source, issues, releases. diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md index 7a67eca1..0d390415 100644 --- a/docs/ecosystem/index.md +++ b/docs/ecosystem/index.md @@ -1,10 +1,71 @@ --- -status: placeholder -wave: 3 +title: Integrations & Ecosystem +description: Companion apps, feeds, and protocol references for building on top of RomM. --- # Integrations & Ecosystem -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +RomM has a sizeable ecosystem of companion apps and integration patterns. This hub indexes everything — first-party and community. + +## First-party apps + +Maintained by the RomM team. + +- **[Argosy Launcher](argosy.md)** — Android launcher that browses and launches your RomM library on mobile. +- **[Grout](grout.md)** — Linux handheld companion for muOS / NextUI devices. +- **[Playnite Plugin](playnite-plugin.md)** — Windows desktop; imports your RomM library into [Playnite](https://playnite.link). +- **[muOS App](muos-app.md)** — official app for muOS / EmulationStation handhelds to fetch games wirelessly. + +## Feeds (for third-party apps) + +RomM exposes several URL feed endpoints for external homebrew / custom firmware apps that already know how to consume them. + +- **[Tinfoil](tinfoil.md)** — Nintendo Switch homebrew for installing `.nsp` / `.xci` from a URL. +- **[pkgj](pkgj.md)** — PS Vita and PSP homebrew installer. +- **[fpkgi](fpkgi.md)** — PS4 / PS5 installer. +- **[Kekatsu](kekatsu.md)** — Nintendo DS multiboot loader. +- **[WebRcade](webrcade.md)** — browser-based retro console frontend. + +See the [full feeds reference](../reference/feeds.md) for URL formats, auth requirements, and filtering. + +## Community apps + +Maintained by individuals in the community, not the RomM team. Support quality varies. + +See **[Community Apps](community-apps.md)** for the full list with status flags (active / maintenance-mode / abandoned) and links. + +Highlights: + +- **romm-ios-app** (iOS native). +- **romm-mobile** (Android + iOS). +- **RommBrowser** (Electron desktop). +- **RomMate** (desktop). +- **romm-client** (desktop). +- **DeckRommSync** (Steam Deck). +- **SwitchRomM** (Nintendo Switch homebrew NRO). +- **RetroArch Sync**. +- **romm-comm** (Discord bot). +- **GGRequestz** (game request tracker). +- **Syncthing Sync**. + +## Build your own + +For developers building something new on top of RomM: + +- **[Client API Tokens](client-api-tokens.md)** — how to authenticate your app, how the device-pairing flow works. +- **[Device Sync Protocol](device-sync-protocol.md)** — wire-level reference for save/state/play-session sync. +- **[API Reference](../developers/api-reference.md)** — every REST endpoint. +- **[WebSockets](../developers/websockets.md)** — live-update channels and Netplay coordination. +- **[Consuming OpenAPI](../developers/openapi.md)** — codegen patterns. + +## External tooling + +Not a RomM companion, but useful alongside: + +- **[Igir Collection Manager](igir.md)** — ROM sorting/verifying tool. Cleans up library layout before importing into RomM. + +## Contributing a companion app + +Built something RomM-adjacent? Open a PR on [rommapp/docs](https://github.com/rommapp/docs) adding it to [Community Apps](community-apps.md), or drop a link in the [Discord](https://discord.gg/romm) `#community-projects` channel. + +We list active, maintained projects. No gate on code quality; we do flag abandoned projects so users know what's current. diff --git a/docs/ecosystem/playnite-plugin.md b/docs/ecosystem/playnite-plugin.md index 7df2e3f4..17278b42 100644 --- a/docs/ecosystem/playnite-plugin.md +++ b/docs/ecosystem/playnite-plugin.md @@ -1,10 +1,115 @@ --- -status: placeholder -wave: 3 +title: Playnite Plugin +description: Import your RomM library into Playnite on Windows. --- + + # Playnite Plugin -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +
+ romm[playnite] logo +
+ +[Playnite](https://playnite.link) is a unified, open-source game library manager for Windows — one place to launch Steam, Epic, GOG, RetroArch, and emulated games. This plugin imports your RomM library into Playnite, so your RomM ROMs appear alongside your Steam games. + +- **Repo:** [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin) +- **Language:** C# +- **License:** GPL-3.0 +- **Platforms:** Windows (Playnite) + +## What it does + +- Queries the RomM API to pull your library metadata. +- Creates Playnite library entries for every RomM game. +- Downloads a ROM on demand when you click **Install** in Playnite. +- Launches via your configured emulator (Playnite's existing emulator config). + +## Installing + +Four paths — pick whichever's easiest: + +- **A.** Paste this URL into a browser to launch Playnite and install automatically: + `playnite://playnite/installaddon/RomM_9700aa21-447d-41b4-a989-acd38f407d9f` +- **B.** [Playnite add-ons website](https://playnite.link/addons.html#RomM_9700aa21-447d-41b4-a989-acd38f407d9f) → Install. +- **C.** From inside Playnite: **Menu → Add-ons → Browse → Libraries**, search `RomM`, Install. +- **D.** Download the `.pext` file from [GitHub Releases](https://github.com/rommapp/playnite-plugin/releases/latest) and drop it onto Playnite. + +## Setup + +### 1. Configure at least one emulator in Playnite + +The plugin needs to know how to launch a game. **If no emulators are configured, setup can't finish.** + +Playnite: **Menu → Library → Configure Emulators → Add emulator**. Add whatever emulators cover your ROMs (Dolphin, RetroArch, Duckstation, etc.). Built-in presets are fine for most common emulators. + +### 2. Configure the plugin + +Playnite: **Menu → Library → Configure Integrations → RomM**. + +#### Authentication + +- **Host URL** — your RomM URL, including scheme, no trailing slash. Examples: + - ✓ `https://romm.example.com` + - ✗ `romm.example.com` + - ✗ `https://romm.example.com/` +- **Username + password** — Playnite stores these in plaintext. Use a **dedicated Viewer-role account** for Playnite, not your admin account. + +!!! tip "Use a Client API Token instead" + If your RomM version supports API tokens, create a Viewer-scoped [Client API Token](client-api-tokens.md) and use it instead of a password. Safer and easier to revoke. + +#### Emulator path mappings + +One mapping per platform: + +| Field | Purpose | Example | Required | +| --- | --- | --- | :---: | +| Emulator | Which Playnite emulator to use | Dolphin | ✓ | +| Emulator Profile | The profile within the emulator | Nintendo GameCube | ✓ | +| Platform | The RomM platform slug | Nintendo GameCube | ✓ | +| Destination Path | Where downloaded ROMs are stored | `C:\roms\gc` | ✓ | +| Auto-extract | Unpack zipped ROMs after download | | — | +| Enabled | Whether this mapping is active | | — | + +Map every platform you want Playnite to see. Platforms without a mapping are skipped during import. + +## Importing your library + +**Menu → Library → Import RomM library.** + +Playnite queries RomM, finds every game matching your emulator path mappings, and creates Playnite library entries for each. Covers and metadata pull through. + +### Installing a game + +Click **Install** on a game card. The plugin: + +1. Downloads the ROM from RomM. +2. Writes it to the destination path for that platform. +3. Optionally extracts (default: on). +4. Marks the game installed in Playnite. + +**Launch** runs the game with the configured emulator, same as any Playnite-managed emulated game. + +### Uninstalling + +**Uninstall** in Playnite deletes the local ROM file and marks the game uninstalled. The RomM-side entry is untouched. + +## Refreshing + +Playnite caches library state. When RomM's library changes, re-run **Import RomM library** to pick up new games. + +For automatic refresh: use Playnite's scheduled-library-refresh add-on, or manually re-import periodically. + +## Troubleshooting + +- **"Unable to connect"** — check the URL scheme, host, and that RomM is reachable from this machine (corporate firewalls, etc.). +- **"Authentication failed"** — username/password mismatch, or the account is disabled. If using a token, make sure it has `roms.read` + `platforms.read`. +- **No games imported** — emulator path mappings don't cover any platforms in your RomM library. Add mappings for your platforms. +- **Install fails** — destination path isn't writable, or disk is full. +- **Wrong emulator launches** — fix the mapping for that platform. + +## See also + +- [Client API Tokens](client-api-tokens.md) — recommended auth method. +- [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin) — source, issues, releases. +- [Playnite docs](https://playnite.link/docs/) — Playnite basics if you're new to it. From 1b3fe291ea77dd04398ff67f97dc66794d36db35 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 18:26:02 +0000 Subject: [PATCH 017/121] docs: finish Wave 3 ecosystem section (7 pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remaining ecosystem section — 4 ports + 3 new feed pages + Igir tool port. - ecosystem/muos-app.md: ported from Integrations/. muOS/ES install paths, .env config, plain-vs-HTTPS guidance, "use Grout for full sync" pointer - ecosystem/tinfoil.md: ported, updated for 5.0 improved title-ID handling. Full reverse-proxy basic-auth pattern as alternative to DISABLE_DOWNLOAD_ENDPOINT_AUTH - ecosystem/pkgj.md: ported. All four pkgi feed URLs (Vita/PSP games/dlcs), config.txt recipe, .pkg-only format requirement - ecosystem/fpkgi.md: NEW. PS4/PS5 .pkg feed, config pointer to upstream fpkgi docs - ecosystem/kekatsu.md: NEW. Nintendo DS homebrew loader, honest legacy-Wi-Fi caveat (DS's WEP-only radio), workaround patterns - ecosystem/webrcade.md: NEW. Alternative browser frontend pointed at RomM's feed, RomM-vs-WebRcade tradeoff guidance - ecosystem/igir.md: ported from Tools/. Expanded with the {romm}-layout igir template, multi-disc .m3u reorg script, read-only RomM mount pattern for the verified output Builds clean with --strict. --- docs/ecosystem/fpkgi.md | 68 +++++++++++++-- docs/ecosystem/igir.md | 173 +++++++++++++++++++++++++++++++++++-- docs/ecosystem/kekatsu.md | 61 +++++++++++-- docs/ecosystem/muos-app.md | 100 +++++++++++++++++++-- docs/ecosystem/pkgj.md | 76 ++++++++++++++-- docs/ecosystem/tinfoil.md | 104 ++++++++++++++++++++-- docs/ecosystem/webrcade.md | 63 ++++++++++++-- 7 files changed, 610 insertions(+), 35 deletions(-) diff --git a/docs/ecosystem/fpkgi.md b/docs/ecosystem/fpkgi.md index 495eb231..2cb77c77 100644 --- a/docs/ecosystem/fpkgi.md +++ b/docs/ecosystem/fpkgi.md @@ -1,10 +1,68 @@ --- -status: placeholder -wave: 3 +title: fpkgi +description: Install PS4 / PS5 packages from your RomM library via fpkgi homebrew. --- # fpkgi -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +[fpkgi](https://github.com/CyberYoshi64/fpkgi) is PS4 / PS5 homebrew for installing `.pkg` packages from custom URL feeds. RomM exposes fpkgi-compatible feeds for its PS4 and PS5 libraries. + +New in RomM 5.0 — earlier versions didn't have fpkgi feeds. + +## Prerequisites + +- **PS4 or PS5** with fpkgi installed (requires CFW / jailbreak — setup is out of scope here). +- **RomM reachable from the console over Wi-Fi** — LAN simplest. +- Games stored as `.pkg` files — fpkgi, like pkgj, only handles the Sony installer format. + +## Feed URL + +```text +{romm_url}/api/feeds/fpkgi/{platform_slug} +``` + +Where `{platform_slug}` is: + +- `ps4` for PlayStation 4 content. +- `ps5` for PlayStation 5 content. + +Example: + +```text +http://192.168.1.100:3000/api/feeds/fpkgi/ps4 +``` + +The feed returns JSON in the fpkgi-expected schema — titles, title IDs, content types, URLs back to RomM for the actual downloads. + +## Configuring fpkgi + +Exact steps depend on the fpkgi version, but the gist: + +1. Put RomM's feed URL in fpkgi's config (usually a JSON file on the console; check fpkgi's own docs). +2. Restart fpkgi. +3. The RomM library appears in fpkgi's browse view. + +Consult [fpkgi's README](https://github.com/CyberYoshi64/fpkgi) for the current config-file location and format — the project moves faster than these docs. + +## Authentication + +The `/api/feeds/fpkgi/` endpoints support basic auth the same way `/api/feeds/pkgi/` does. Either: + +- Set basic-auth credentials in fpkgi's config if it supports them, OR +- Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` on the RomM server (see the [Tinfoil](tinfoil.md) caveat about public exposure). + +## File format — must be `.pkg` + +PS4 `.pkg` files specifically — not `.iso`, not compressed. RomM filters to `.pkg` when building the feed. Any other file types are invisible to fpkgi. + +## Troubleshooting + +- **Feed is empty** — no `.pkg` files on the `ps4` / `ps5` platform. Check your library. +- **Downloads fail with 401** — auth config mismatch. See Authentication section above. +- **Downloads succeed but install fails** — `.pkg` is for a different firmware version. Not a RomM problem. + +## See also + +- [Feeds reference](../reference/feeds.md) — all feed endpoints. +- [pkgj](pkgj.md) — PS Vita / PSP equivalent. +- [fpkgi upstream](https://github.com/CyberYoshi64/fpkgi) — installer homebrew. diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md index 37737b22..47a5892d 100644 --- a/docs/ecosystem/igir.md +++ b/docs/ecosystem/igir.md @@ -1,10 +1,173 @@ --- -status: placeholder -wave: 3 +title: Igir Collection Manager +description: Clean up and normalise your ROM collection with Igir before importing into RomM. --- # Igir Collection Manager -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +[Igir](https://igir.io/) is a zero-setup ROM collection manager — sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se; more a pre-processing tool. Useful for cleaning up a library *before* importing into RomM, so RomM's scans have a better-named, better-organised starting point. + +**This is not an official RomM app.** Igir is a separate community project. We document integration here because it's a common workflow and produces a RomM-compatible layout directly. + +## When you'd use Igir + +- You have a messy collection — inconsistent naming, mixed formats, dumps from multiple sources. +- You want to **match against No-Intro / Redump DAT files** to verify authenticity and standardise names. +- You want to **filter** — only retail releases, strip out hacks, keep only one region, etc. +- You want to move / rename files to RomM's expected platform folder layout. + +If your library is already clean, skip Igir. RomM's scans handle naming variations gracefully. + +## Directory setup + +Igir works on a copy of your ROMs — never in place — to let you iterate on its config without risking the originals. + +```text +. +├── dats/ # DAT files (No-Intro, Redump) +├── roms/ # Your original ROM collection (untouched) +├── roms-unverified/ # Working copy Igir will process +└── igir-romm-cleanup.sh # the script below +``` + +### 1. Make a working copy + +```sh +cp -r roms/ roms-unverified/ +``` + +### 2. Download DAT files + +DAT files are hash-referenced catalogues Igir matches against. + +- **Cartridge systems:** [No-Intro daily](https://datomatic.no-intro.org/index.php?page=download&op=daily) — full DAT compilation. +- **Optical systems (PS1, Saturn, etc.):** [Redump](http://redump.org/downloads/) — per-platform DAT files. + +Drop the DAT files into `dats/`. You can use a subset if you only care about specific platforms. + +## The cleanup script + +Save as `igir-romm-cleanup.sh`: + +```bash +#!/usr/bin/env bash +set -ou pipefail +cd "$(dirname "${0}")" + +INPUT_DIR=roms-unverified +OUTPUT_DIR=roms-verified + +# https://igir.io/ +# DAT files: https://datomatic.no-intro.org/index.php?page=download&op=daily +time npx -y igir@latest \ + move \ + extract \ + report \ + test \ + -d dats/ \ + -i "${INPUT_DIR}/" \ + -o "${OUTPUT_DIR}/{romm}/" \ + --input-checksum-quick false \ + --input-checksum-min CRC32 \ + --input-checksum-max SHA256 \ + --only-retail +``` + +Make it executable: + +```sh +chmod +x igir-romm-cleanup.sh +``` + +### What it does + +- `move extract` — extract archives and move the results. +- `test` — verify checksums against DATs. +- `report` — generate a markdown report of matches / misses. +- `-o ${OUTPUT_DIR}/{romm}/` — output in RomM's expected platform layout (`{romm}` is Igir's RomM-layout template). +- `--only-retail` — exclude betas, hacks, unlicensed dumps. +- `--input-checksum-*` — check files thoroughly (slow but authoritative). + +## Run + +```sh +./igir-romm-cleanup.sh +``` + +Output: + +- `roms-verified/{platform-slug}/{Proper Game Name}.rom` — identified ROMs in RomM layout. +- `roms-unverified/` — whatever Igir didn't identify, still available for manual review. +- `report.csv` (or similar) — what matched, what didn't. + +## Manually migrate leftovers + +Some ROMs Igir won't identify (homebrew, hacks with `--only-retail`, truly unknown dumps). Move them manually preserving the folder shape: + +```sh +npx -y igir@latest \ + move \ + -i roms-unverified/ \ + -o roms-verified/ \ + --dir-mirror +``` + +This keeps the original subfolder structure but normalises extensions. + +## Multi-disc reorganisation + +Igir outputs multi-disc games as separate folders, which confuses RomM's multi-file game detection. Collapse them: + +```sh +cd roms-verified/ps # or psx, or whatever your PSX slug is + +ls -d *Disc* | while read file; do + game=$(echo "${file}" | sed -E 's/ ?\(Disc.*//') + mkdir -p "${game}" + mv "${file}" "${game}" + m3u="${game}/${game}.m3u" + touch "${m3u}" + echo "${file}" >> "${m3u}" +done +``` + +Before: + +```text +Final Fantasy VII (Disc 1) (USA)/ +Final Fantasy VII (Disc 2) (USA)/ +Final Fantasy VII (Disc 3) (USA)/ +``` + +After: + +```text +Final Fantasy VII (USA)/ + Final Fantasy VII (Disc 1) (USA)/ + Final Fantasy VII (Disc 2) (USA)/ + Final Fantasy VII (Disc 3) (USA)/ + Final Fantasy VII (USA).m3u +``` + +The `.m3u` is a playlist RomM respects for launching multi-disc games. + +## Import into RomM + +Once `roms-verified/` looks right, mount it as RomM's library: + +```yaml +services: + romm: + volumes: + - /path/to/roms-verified:/romm/library/roms:ro +``` + +Read-only is safer — if you need Igir to re-clean, you work in the parallel `roms-unverified/` and re-promote to `roms-verified/`. + +Run a scan from RomM. Everything should match cleanly against providers. + +## See also + +- [Igir docs](https://igir.io/) — the upstream reference. +- [Folder Structure](../getting-started/folder-structure.md) — what RomM expects on-disk. +- [Metadata Providers](../administration/metadata-providers.md) — how RomM matches after Igir's done its work. diff --git a/docs/ecosystem/kekatsu.md b/docs/ecosystem/kekatsu.md index b5df388e..a70f56a1 100644 --- a/docs/ecosystem/kekatsu.md +++ b/docs/ecosystem/kekatsu.md @@ -1,10 +1,61 @@ --- -status: placeholder -wave: 3 +title: Kekatsu +description: Nintendo DS multiboot loader — install DS games from RomM via custom feed. --- # Kekatsu -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +**Kekatsu** is Nintendo DS homebrew for loading games from a custom URL feed. RomM exposes a Kekatsu-compatible feed for its DS library. + +New in RomM 5.0. + +## Prerequisites + +- A Nintendo DS with Kekatsu installed (requires a flashcart or homebrew launcher). +- **RomM reachable from the DS over Wi-Fi** — the DS's Wi-Fi is WEP / old WPA only, so this typically means a dedicated legacy-SSID on your router or a travel router bridging the DS to your modern network. +- DS games in `.nds` format. + +## Feed URL + +```text +{romm_url}/api/feeds/kekatsu/{platform_slug} +``` + +For standard DS content, the platform slug is `nds`: + +```text +http://192.168.1.100:3000/api/feeds/kekatsu/nds +``` + +## Configuring Kekatsu + +Exact config steps depend on your Kekatsu build; the shared concept is "point the app at this URL and it fetches the manifest". Consult Kekatsu's own docs for the current config-file location. + +## File format + +`.nds` only. DSi Ware / iQue / other formats aren't listed in the feed. + +## Authentication + +Kekatsu can send basic auth. Either configure it on the DS side or enable `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` on RomM (see the [Tinfoil download-auth caveat](tinfoil.md#prerequisites)). + +## Why the legacy-Wi-Fi hassle + +The DS's original Wi-Fi hardware supports WEP and an older WPA variant only. Modern home routers usually don't. Workarounds: + +- **Dedicated DS-friendly SSID.** Many routers allow per-SSID security — add a WEP one just for the DS. +- **Travel router in bridge mode.** A cheap travel router configured for WEP uplinks to your main (secure) network. +- **Use a DSi, 3DS, or homebrew replacement driver** — these support modern security. + +If none of this is appealing, Kekatsu-over-LAN isn't going to work; fall back to sideloading via flashcart or similar. + +## Troubleshooting + +- **Feed is empty** — no `.nds` files on the `nds` platform. +- **DS can't see the network** — see the legacy-Wi-Fi section above. +- **Downloads fail** — either network timeout (LAN latency over WEP is rough) or disk space. Retry one game at a time. + +## See also + +- [Feeds reference](../reference/feeds.md) — all feed endpoints. +- Kekatsu upstream — find via the RomM Discord `#kekatsu` or community links (project moves). diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md index bfb2bfc8..07f36921 100644 --- a/docs/ecosystem/muos-app.md +++ b/docs/ecosystem/muos-app.md @@ -1,10 +1,100 @@ --- -status: placeholder -wave: 3 +title: muOS App +description: Official RomM app for muOS and EmulationStation handhelds — fetch games wirelessly. --- + + # muOS App -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +
+ RomM muOS logo +
+ +[muOS](https://muos.dev) is a custom firmware (CFW) for handheld devices — Anbernic, Miyoo, and similar. The **muOS App** connects to your RomM instance and fetches ROMs wirelessly. + +- **Repo:** [rommapp/muos-app](https://github.com/rommapp/muos-app) +- **Platforms:** muOS, EmulationStation (via PortMaster) +- **Use case:** fetch games on-demand from RomM without cable-swapping SD cards. + +## muOS-specific flavour vs Grout + +This page covers the **muOS App** — a lightweight client focused on game fetching. For the fuller push/pull sync experience (saves back to RomM, play-session reporting), use [Grout](grout.md) instead. They're two different clients for the same family of devices. + +- **muOS App** — lightweight; pulls ROMs, no save sync. +- **Grout** — full sync; ROMs + saves + states + play sessions. + +Pick based on what you need. + +## Installing — muOS + +Installation uses muOS's [Archive Manager](https://muos.dev/installation/archive): + +1. Download the latest `RomM.muOS.x.x.x.muxapp` from [GitHub Releases](https://github.com/rommapp/muos-app/releases/latest). +2. Move the `.muxapp` file to `/mnt/mmc/ARCHIVE/` on the device (USB, SD swap, or SSH). +3. On the device: **Applications → Archive Manager** → select `RomM.muOS.x.x.x.muxapp` → install. +4. Once installed, copy `/mnt/mmc/MUOS/application/RomM/env.template` to `.env` in the same folder. +5. Edit `.env` (SSH works well; so does any method that writes to SD card): + + ```dotenv + HOST=http://192.168.1.100:3000 + USERNAME=yourusername + PASSWORD=yourpassword + ``` + +6. Launch from **Applications → RomM** on the device. + +!!! tip "Use a dedicated account or token" + The username/password lives in plaintext on the SD card. Use a dedicated Viewer-role account for the handheld, not your admin credentials. + + Once RomM's 5.0 Client API Token flow is wired into the muOS app (planned), prefer pairing via token instead of password. + +## Installing — EmulationStation (via PortMaster) + +For EmulationStation-based devices: + +1. Download the `RomM App.sh` file and the `RomM/` folder. +2. Copy to `roms/ports/` on the device. +3. SSH / shell in: `chmod +x "RomM App.sh"`. +4. Launch EmulationStation → **Ports** → RomM App. + +## Network requirements + +The handheld has to reach your RomM instance over Wi-Fi. + +Simplest setup: + +- **Same LAN.** Handheld and RomM server on the same SSID. `HOST` = server IP + port. +- **Plain HTTP works** on a trusted LAN; no reverse proxy needed. + +More-involved setups: + +- **Reverse proxy with TLS.** `HOST=https://romm.example.com`. HTTPS works but introduces cert-validation risk on handhelds (some fail strict TLS). +- **Remote access via VPN.** Install Tailscale or similar on the handheld (if supported) — lets the handheld reach RomM from outside the LAN. + +## Using the app + +Launch RomM app → browse platforms → select a game → download. + +Downloaded games appear in the device's usual ROM folder for the platform, so muOS / ES picks them up on the next library refresh. + +## What it doesn't do (yet) + +- **Save sync** — this app is pull-only. For bidirectional sync, use [Grout](grout.md). +- **Play session tracking** — not ingested into RomM. +- **Firmware download** — not in scope. + +If you need these, Grout is the app. + +## Troubleshooting + +- **Can't connect** — wrong `HOST` in `.env`, or the handheld isn't on the same network as RomM. Ping RomM's IP from the handheld's shell to confirm reachability. +- **"Authentication failed"** — password wrong, or `DISABLE_USERPASS_LOGIN=true` on the RomM side. Either re-enable user/pass login or use a token once supported. +- **Downloaded games don't show in the platform** — refresh the library from muOS's UI. If they still don't appear, the platform folder in `HOST_PATH` is wrong — check muOS's expected layout. + +## See also + +- [Grout](grout.md) — the fuller sync client for the same device family. +- [Client API Tokens](client-api-tokens.md) — safer auth than plaintext credentials in `.env`. +- [Mobile & TV](../using/mobile-and-tv.md) — handheld usage patterns. +- [rommapp/muos-app](https://github.com/rommapp/muos-app) — source, issues, releases. diff --git a/docs/ecosystem/pkgj.md b/docs/ecosystem/pkgj.md index e046870e..4b7ffa3e 100644 --- a/docs/ecosystem/pkgj.md +++ b/docs/ecosystem/pkgj.md @@ -1,10 +1,76 @@ --- -status: placeholder -wave: 3 +title: pkgj +description: Install PS Vita and PSP games from your RomM library via pkgj homebrew. --- # pkgj -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +[pkgj](https://github.com/blastrock/pkgj) is PS Vita homebrew for installing `.pkg`-format games and DLC. Default config points at well-known community URLs; point it at RomM's feed endpoints instead and you can install from your library over Wi-Fi. + +## Prerequisites + +- **PS Vita** with [pkgj](https://github.com/blastrock/pkgj) installed. +- A way to edit files on the Vita — [VitaShell](https://github.com/TheOfficialFloW/VitaShell) works well. +- **RomM reachable from the Vita** — same LAN ideal; HTTP or HTTPS both work. +- Your games stored as `.pkg` files (pkgj requires this format — it won't work with `.iso` or other formats). + +## Feed URLs + +RomM exposes four pkgi-compatible feeds: + +| Content | URL | +| --- | --- | +| PS Vita games | `{romm_url}/api/feeds/pkgi/psvita/game` | +| PS Vita DLCs | `{romm_url}/api/feeds/pkgi/psvita/dlc` | +| PSP games | `{romm_url}/api/feeds/pkgi/psp/game` | +| PSP DLCs | `{romm_url}/api/feeds/pkgi/psp/dlc` | + +## Configuring pkgj + +1. Connect the Vita to your PC over USB with VitaShell running. +2. Open `/pkgj/config.txt` on the Vita in a text editor. +3. Append lines for each feed you have content for: + + ```ini + url_games {romm_url}/api/feeds/pkgi/psvita/game + url_dlcs {romm_url}/api/feeds/pkgi/psvita/dlc + url_psp_games {romm_url}/api/feeds/pkgi/psp/game + url_psp_dlcs {romm_url}/api/feeds/pkgi/psp/dlc + ``` + + Replace `{romm_url}` with your actual RomM URL (e.g. `http://192.168.1.100:3000`). + +4. Save and disconnect from VitaShell. +5. On the Vita: open pkgj → press `△` to open the menu → **Refresh**. + +## Using pkgj + +Once configured, pkgj shows your RomM PS Vita / PSP library. Select a title → install. pkgj downloads the `.pkg` from RomM and installs it to the Vita. + +## File format requirements + +**RomM only lists `.pkg` files in pkgi feeds.** If your Vita or PSP games are in `.iso`, `.chd`, or other formats, they won't appear in pkgj. + +Why: pkgj is designed around the Sony installer format. Other formats aren't installable via the same mechanism. + +If you have non-`.pkg` files you want on the Vita, you'll need to convert them or use a different workflow (FTP through VitaShell, for example). + +## Authentication + +The pkgi feeds honour basic auth. If your RomM doesn't have `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`, pkgj sends basic auth headers. + +Unlike [Tinfoil](tinfoil.md), pkgj handles auth natively — you don't have to turn off download auth on RomM to use it. Still, some users prefer disabling auth for a smoother first-time flow; either path works. + +## Troubleshooting + +- **"can't get list: list is empty"** — you don't have any `.pkg` content for the feeds you configured. Check your library actually contains `.pkg` files on the corresponding platforms. +- **Refresh returns an error** — URL in `config.txt` is wrong. Verify the feed URL in a browser first (returns JSON). +- **Download fails partway** — LAN connectivity or disk space on Vita. pkgj reports both. +- **Game installs but won't boot** — the `.pkg` is for a different firmware / region. Not a RomM issue. + +## See also + +- [Feeds reference](../reference/feeds.md) — full feeds catalogue. +- [Tinfoil](tinfoil.md) — Switch equivalent. +- [fpkgi](fpkgi.md) — PS4 / PS5 equivalent. +- [pkgj](https://github.com/blastrock/pkgj) — upstream homebrew. diff --git a/docs/ecosystem/tinfoil.md b/docs/ecosystem/tinfoil.md index 2d1aa7d9..95efcccc 100644 --- a/docs/ecosystem/tinfoil.md +++ b/docs/ecosystem/tinfoil.md @@ -1,10 +1,104 @@ --- -status: placeholder -wave: 3 +title: Tinfoil +description: Install Nintendo Switch games from your RomM library over Wi-Fi via Tinfoil's feed mechanism. --- + + # Tinfoil -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +
+ RomM Tinfoil logo +
+ +[Tinfoil](https://tinfoil.io) is Nintendo Switch homebrew for installing software from custom feed URLs. Point it at RomM's Tinfoil feed endpoint, and your Switch library becomes installable from the Switch itself over Wi-Fi. + +## Prerequisites + +- **RomM 3.5.0 or newer.** Tinfoil feeds landed in that release. Much better in 5.0. +- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`** on your RomM instance. Tinfoil can't send a bearer token, so the downloads endpoint has to be openable. **Only enable this when RomM isn't directly exposed to the public internet** — see [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass). +- **Tinfoil installed on the Switch.** Setup varies; follow Tinfoil's own docs. +- **A Switch that can reach RomM over Wi-Fi.** Same LAN is easiest; remote reachability requires reverse proxy + cert that the Switch accepts. + +## Feed URL + +```text +{romm_url}/api/feeds/tinfoil +``` + +No authentication — the endpoint works as long as `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`. + +## Configuring Tinfoil + +1. Launch Tinfoil on the Switch → **File Browser**. +2. Scroll to the file-server list → press `-` (minus) to add a new one. +3. Enter: + - **Protocol:** `http` or `https` (depending on your RomM reverse-proxy setup). + - **Host:** RomM's hostname or IP. + - **Port:** RomM's port (usually 80 or 443). + - **Path:** `/api/feeds/tinfoil` + - **Username:** your RomM username (optional — Tinfoil can send basic auth; RomM tries it). + - **Password:** your RomM password. + - **Title:** anything (e.g. `RomM Switch`). + - **Enabled:** yes. +4. Press `X` to save. +5. Close and reopen Tinfoil. The library is parsed. + +On reopen, you should see a custom message of the day: `RomM Switch Library`. If you do, it's working. + +![Tinfoil after setup](../resources/tinfoil/tinfoilscreen.jpg) + +## Using it + +- **New Games** tab in Tinfoil — browseable list of your Switch ROMs. +- **File Browser** — pick a file to install directly. + +Tinfoil handles the install flow like any homebrew: downloads the `.nsp` / `.xci`, installs to eMMC or SD, cleans up. + +## Filename requirements — TitleIDs + +Tinfoil needs **Switch title IDs** in the filenames to parse and categorise games. The format: + +```text +Super Mario Odyssey [0100000000010000][v0].nsp +``` + +The bracketed `[0100000000010000]` is the title ID. Without it, Tinfoil shows the file but doesn't parse it into the New Games tab or match against Switch metadata. + +![TitleID example](../resources/tinfoil/titleid.jpg) + +!!! info "Improvement coming" + 5.0 improves RomM's title-ID handling — it auto-detects title IDs from filenames that have them and feeds Tinfoil regardless of whether your filename format is standard. The guidance above still applies for older RomM releases. + +### Finding title IDs + +- [No-Intro](https://datomatic.no-intro.org/) lists them per title. +- [tinfoildb.com](https://tinfoildb.com/) and similar databases are searchable. +- Pre-organised ROM sets ship with title IDs in filenames already. + +### Renaming files + +Once renamed, the next time Tinfoil opens it re-parses. + +## Alternative: through a reverse proxy + +If you put RomM behind a reverse proxy, the proxy can handle auth separately from RomM. For example: + +- Proxy challenges for basic auth before reaching RomM. +- RomM itself has `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`. + +Tinfoil sends basic auth upstream; proxy accepts and forwards; RomM serves. + +This gets you authenticated Tinfoil feeds without making RomM itself world-readable. + +## Troubleshooting + +- **"can't get list: list is empty"** — either your RomM library has no `.nsp`/`.xci` files that Tinfoil recognises, or filenames lack title IDs. +- **Tinfoil connects but nothing in New Games** — title IDs missing from filenames. Rename. +- **Tinfoil can't connect at all** — LAN reachability issue, or wrong port in the feed setup. Try `http://:/api/feeds/tinfoil` in a browser; you should get JSON. +- **Downloads fail with 401** — `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` isn't set on RomM, or you forgot to restart the container after setting it. + +## See also + +- [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass) — the `DISABLE_DOWNLOAD_ENDPOINT_AUTH` caveat. +- [Feeds reference](../reference/feeds.md) — full feeds catalogue. diff --git a/docs/ecosystem/webrcade.md b/docs/ecosystem/webrcade.md index adf4e9d2..e1f77ad6 100644 --- a/docs/ecosystem/webrcade.md +++ b/docs/ecosystem/webrcade.md @@ -1,10 +1,63 @@ --- -status: placeholder -wave: 3 +title: WebRcade +description: Load your RomM library into WebRcade — an alternative browser-based retro frontend. --- # WebRcade -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +[WebRcade](https://www.webrcade.com/) is a browser-based retro console frontend. It plays games in-browser, similar to RomM's built-in EmulatorJS player — the difference is UX: WebRcade has its own aesthetic, a curated preset-app model, and feed-based import. + +If you prefer WebRcade's look-and-feel but want to point it at your RomM library, use the feed endpoint below. + +## Feed URL + +```text +{romm_url}/api/feeds/webrcade +``` + +WebRcade-compatible JSON listing every ROM in your library with metadata and direct download URLs back to RomM. + +## Setting up WebRcade + +1. Open [WebRcade.com](https://www.webrcade.com/) in a browser. +2. Create an account (or use it unauthenticated — limited features). +3. Go to **My WebRcade → Add Feed**. +4. Enter RomM's feed URL: `{romm_url}/api/feeds/webrcade`. +5. Save. Your RomM library appears as WebRcade apps. + +## Authentication + +The `/api/feeds/webrcade` endpoint sends basic auth if WebRcade provides credentials. Either: + +- Configure basic auth on WebRcade's feed-add screen (if it offers that), OR +- Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` on RomM. + +The same security caveats apply — see [Tinfoil prerequisites](tinfoil.md#prerequisites) for context on turning off download auth. + +## RomM vs WebRcade + +Why would you use WebRcade over RomM's built-in player? + +- **Preset/curated lists.** WebRcade maintains a catalogue of vetted content that's searchable inside the app. +- **Different UI.** More console-like, less library-like. +- **Per-game launch from WebRcade feeds.** You can mix-and-match RomM content with WebRcade's own catalogue in one frontend. + +When to stay with RomM's player: + +- **You want library management.** WebRcade is frontend-only; RomM owns the metadata and scanning. +- **You want user accounts + collections + per-user progress.** WebRcade is single-user-ish. +- **You want [Netplay](../using/netplay.md)** — RomM has it; WebRcade doesn't. + +Totally reasonable to run both — WebRcade as a launcher UI pointed at RomM for the library. + +## Troubleshooting + +- **Feed shows no games** — WebRcade filters by its own supported-platform set. Games on unsupported platforms won't show. +- **Games don't launch** — the ROM format isn't supported by WebRcade's emulator cores (which differ from EmulatorJS's). Check WebRcade's supported list. +- **Download fails** — same auth / URL issues as other feeds. Check `{romm_url}/api/feeds/webrcade` returns JSON in a browser. + +## See also + +- [Feeds reference](../reference/feeds.md) — all feed endpoints. +- [In-Browser Play](../using/in-browser-play.md) — RomM's built-in player. +- [WebRcade](https://www.webrcade.com/) — upstream frontend. From 3486adafc5aba9113807384b0c9c93817ea531d9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 18:31:32 +0000 Subject: [PATCH 018/121] docs: draft Wave 3 developers advanced pages (6 pages) Finishes the developers/ section (minus api-reference.md still pending the OpenAPI snapshot infrastructure). - developers/index.md: hub organising the section by "working with the API / building companion apps / contributing / reference", plus a quick-orientation table - developers/websockets.md: Socket.IO endpoint catalogue (/ws and /netplay namespaces), server-to-client event list, Redis-backed multi-instance notes, reverse-proxy passthrough requirements, Python socketio client example - developers/openapi.md: where /openapi.json lives, snapshotting per release, client codegen recipes (openapi-generator for Python + TypeScript), Postman/Insomnia/Bruno import, schema validation libraries, known spec quirks - developers/architecture.md: runtime topology ASCII diagram, nginx + gunicorn + FastAPI + RQ workers + MariaDB + Redis parts, request lifecycle walkthroughs (browse, upload, scan), frontend Vue + Pinia + i18n structure, Console Mode separate SPA, background-work RQ queue priorities, why-these-choices - developers/i18n.md: vue-i18n structure, locale folders, how to add keys to existing locale or introduce a new one, style guidance (tone/consistency/interpolation/pluralization), RTL caveat, PR conventions - developers/releasing.md: maintainer reference. Release cadence, SemVer rules, full pre-release checklist (versions, env.template, changelog, tag+push), Docker image + GitHub Release publishing, mike docs-version alias promotion, post-release patch flow, security-release protocol, breaking-change protocol Builds clean with --strict (after fixing one anchor typo). --- docs/developers/architecture.md | 175 +++++++++++++++++++++++++++++++- docs/developers/i18n.md | 150 ++++++++++++++++++++++++++- docs/developers/index.md | 52 +++++++++- docs/developers/openapi.md | 101 +++++++++++++++++- docs/developers/releasing.md | 167 +++++++++++++++++++++++++++++- docs/developers/websockets.md | 148 ++++++++++++++++++++++++++- 6 files changed, 763 insertions(+), 30 deletions(-) diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index 90904db3..79e1a4df 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -1,10 +1,175 @@ --- -status: placeholder -wave: 3 +title: Architecture +description: High-level walkthrough of the RomM codebase — backend, frontend, nginx, workers. --- # Architecture -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +A walkthrough of how RomM is put together, aimed at first-time contributors. Enough to orient yourself before diving in. + +## Repo layout + +```text +rommapp/romm +├── backend/ # Python FastAPI application +├── frontend/ # Vue 3 + Vuetify SPA (main UI) +├── docker/ # nginx config, entrypoint, Dockerfiles +├── examples/ # Reference docker-compose.yml and config.yml +├── env.template # Env var reference +└── pyproject.toml # Backend Python deps +``` + +Key sub-directories: + +- `backend/routers/` — FastAPI route definitions, one file per resource group. +- `backend/handlers/` — business logic (scan engine, metadata providers, auth). +- `backend/tasks/` — RQ job definitions (scheduled + manual + watcher tasks). +- `backend/config/` — Pydantic config schemas for `config.yml`. +- `backend/alembic/` — DB migrations. +- `backend/romm_test/` — test suite. +- `frontend/src/views/` — top-level Vue pages. +- `frontend/src/console/` — Console Mode SPA (separate entry). +- `frontend/src/stores/` — Pinia state stores. + +## Runtime topology + +A running RomM container hosts several cooperating processes: + +``` +┌─────────────────────────────────────────────────────────┐ +│ docker container │ +│ │ +│ ┌───────┐ HTTP ┌───────┐ python ┌───────────┐ │ +│ │ nginx │────────→│ gunicorn│──→│ FastAPI │ │ +│ │ :8080 │ │ :5000 │ │ backend │ │ +│ └───┬───┘ └─────────┘ └────┬────┘ │ +│ │ static files SQL │ │ +│ │ (EmulatorJS, │ │ +│ │ Ruffle, SPA) ↓ │ +│ │ ┌─────────┐ │ +│ │ │ MariaDB │(external) │ +│ ↓ │ (replica)│ │ +│ ┌──────┐ └─────────┘ │ +│ │ serve│ │ +│ │ /library│←── bind mount from host │ +│ │ /assets │ read/write │ +│ │ /resources│ │ +│ └──────┘ │ +│ │ +│ ┌────────┐ ┌────────┐ │ +│ │ RQ │←──────────→│ Redis │ │ +│ │ workers│ tasks │ (embedded or external) │ +│ └────────┘ └────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +### The parts + +- **nginx** — listens on `8080`. Serves static assets (the SPA bundle, EmulatorJS core files, Ruffle, cover images). Proxies API + WebSocket traffic to gunicorn. Streams large downloads via `mod_zip`. +- **gunicorn** — the Python WSGI server, running the FastAPI app. Multiple worker processes; `WEB_SERVER_CONCURRENCY` tunes count. +- **FastAPI backend** — routes, handlers, DB access via SQLAlchemy, Redis for sessions + cache + queue. +- **RQ workers** — separate process(es) that pop jobs off Redis queues and run them. Scans, metadata syncs, cleanup tasks. +- **Redis / Valkey** — in-container by default (full image), externalisable. +- **MariaDB / Postgres / MySQL / SQLite** — always external (or a separate container). + +## Request lifecycle + +### Browsing the library (GET `/api/roms`) + +1. Browser sends request → reverse proxy → nginx. +2. nginx forwards to gunicorn → FastAPI → `backend/routers/roms.py`. +3. Handler queries MariaDB via SQLAlchemy. +4. Response serialised, returned. + +Simple. Same flow for every non-WebSocket API call. + +### Uploading a ROM (POST `/api/roms/upload/*`) + +1. Browser → nginx. nginx buffers up to `client_max_body_size`. +2. gunicorn → FastAPI → `backend/routers/roms_upload.py`. +3. Handler writes chunks to a temp path, assembles on complete. +4. Moved into place under `/romm/library`. +5. DB entry created. +6. Socket.IO emits `rom:created`. + +Large uploads go through the chunked-upload flow for a reason: gunicorn's per-request memory ceiling would otherwise be an issue. + +### Scanning + +1. Trigger: UI button, `/api/tasks/run/scan`, scheduled cron, or watcher event. +2. Handler enqueues an RQ job. +3. Worker pops job, runs `handlers/scan.py`: + - Walks filesystem. + - Hashes files. + - Calls metadata providers in parallel. + - Writes to DB. +4. Emits `scan:progress` and `scan:log` via Socket.IO during execution. +5. Emits `scan:complete` when done. + +## Frontend architecture + +### Main UI (`frontend/src/`) + +- **Vue 3 + Composition API**. +- **Vuetify 3** for UI components. +- **Vite** for build + dev server with HMR. +- **Pinia** for state management — one store per resource family (auth, roms, platforms, collections, etc.). +- **vue-i18n** for localisation. Translations in `src/locales//`. +- **socket.io-client** for live updates. + +Router in `src/plugins/router.ts`. Top-level views in `src/views/`. + +### Console Mode SPA (`frontend/src/console/`) + +Separate SPA, compiled as a second bundle. Entry point at `/console`. Built with the same Vue + Pinia + i18n stack but: + +- Own router with `/console` namespace. +- Own component library tuned for gamepad navigation (bigger targets, spatial focus, SFX). +- Own theme in `src/console/styles/`. + +## Background work + +RQ has three priority queues: + +- **high** — user-triggered scans, manual tasks. Get fast. +- **default** — scheduled nightlies, sync operations. +- **low** — cleanup, image conversion. Run when the system's idle. + +All queues share the same worker pool. Jobs are Redis-backed, so restarting RomM doesn't lose in-flight work (appendonly needs to be on — see [Redis or Valkey](../install/redis-or-valkey.md)). + +## Auth + +Session state in Redis. Passwords bcrypt-hashed in the DB. JWT for OAuth2 bearer tokens. + +OIDC handled via `authlib` on the backend. Session cookies issued after successful OIDC callback; from there, it's a regular session. + +Client API Tokens are stored as hash-only in the DB (we never store the plaintext token after creation). A token is validated by hashing the presented bearer and comparing. + +## Observability + +- **Sentry** (opt-in) — unhandled exceptions. +- **OpenTelemetry** (opt-in) — traces, metrics, logs via OTLP. +- **`/api/heartbeat`** — aggregated health snapshot. + +See [Observability](../administration/observability.md). + +## Why these choices + +- **FastAPI**: modern, async, auto-OpenAPI. Good fit for the API-shape of the app. +- **Vue + Vuetify**: fast, componenty, looks decent out of the box. +- **MariaDB** as default: solid, familiar, works everywhere. +- **RQ** not Celery: simpler, Redis-native, less operational overhead. Works fine at RomM's scale. +- **Socket.IO**: pragmatic. Raw WebSocket would be leaner but SIO's client ecosystem is worth the overhead. + +## Contributing + +See [Contributing](contributing.md) for the process + style expectations. + +For large changes, read the relevant handler in `backend/handlers/` first — the patterns there will guide what you write. + +## See also + +- [Development Setup](development-setup.md) — get a local env running. +- [API Reference](api-reference.md) — what the backend exposes. +- [WebSockets](websockets.md) — Socket.IO endpoints in detail. +- [Configuration File](../reference/configuration-file.md) — `config.yml` schema. diff --git a/docs/developers/i18n.md b/docs/developers/i18n.md index 6c999d63..3cf014ce 100644 --- a/docs/developers/i18n.md +++ b/docs/developers/i18n.md @@ -1,10 +1,150 @@ --- -status: placeholder -wave: 3 +title: Translations (i18n) +description: Contributor guide for adding or improving RomM translations. --- # Translations (i18n) -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +End users picking a language: [Languages](../using/languages.md). + +This page is for **contributors** adding or improving translations. + +## What RomM supports + +- **App UI** is translated. 19 locales currently ship. +- **Docs** (what you're reading) are English-only in 5.0. +- **Game metadata** isn't translated by RomM — that comes from metadata providers (IGDB, ScreenScraper). Their localisation varies per title. + +## Tech stack + +- **Library**: [vue-i18n](https://vue-i18n.intlify.dev/). +- **File format**: JSON (one file per locale). +- **Location**: `frontend/src/locales//`. + +Each locale folder contains translation files. `en_US/` is the reference — always kept complete and current. Other locales may have partial coverage (missing keys fall back to `en_US`). + +## Adding keys to an existing locale + +Scenario: the English UI has a string that isn't translated in your locale. Fill it in. + +1. Fork [rommapp/romm](https://github.com/rommapp/romm). +2. Open `frontend/src/locales/en_US/` and find the untranslated key. +3. Open the corresponding file in `frontend/src/locales//`. +4. Add the key with your translation. +5. Run `npm run dev` from `frontend/` to see it live. +6. Open a PR. + +### Example + +`en_US/common.json`: + +```json +{ + "scan": "Scan", + "continue_playing": "Continue Playing" +} +``` + +`fr_FR/common.json` (missing translations): + +```json +{ + "scan": "Analyser" +} +``` + +After your contribution: + +```json +{ + "scan": "Analyser", + "continue_playing": "Continuer à jouer" +} +``` + +## Adding a new locale + +Scenario: the language you want isn't in the dropdown yet. + +1. Pick your locale code. Use [BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) format — `de_DE`, `pt_BR`, `zh_TW`. The underscore convention matches existing folders. +2. Copy `frontend/src/locales/en_US/` to `frontend/src/locales//`. +3. Translate the strings. Start with the most-visible ones (menu, navigation, home dashboard) — partial coverage is welcome. +4. Register the locale in the index file (filename varies; look for something like `frontend/src/locales/index.ts` that lists active locales). +5. Run `npm run dev` and verify your locale appears in the Language dropdown. +6. Open a PR. + +**Partial translations are merged.** Don't hold out for 100% — English fallback handles gaps gracefully. A 60%-translated Arabic is better than no Arabic. + +## Style guidance + +### Tone + +Match the source tone: + +- **Conversational but not too casual.** "Let's scan your library" is fine; "Yo, scan your roms dude" isn't. +- **Active voice.** "Scan the library", not "The library is being scanned". +- **Consistent formality.** Pick `tu`/`vous`, formal/informal `you`, etc., and stick with it across the locale. + +### Consistency + +Use the same word for the same concept throughout. If you translate "Collection" as one word at the top of a file, don't translate it differently three screens later. The `en_US` file has a glossary at the top for key terms — mirror that pattern in your locale. + +### Interpolations + +vue-i18n uses `{placeholder}` syntax. **Don't translate placeholder names.** + +```json +{ + "hello_user": "Hello, {username}!", + "n_games_found": "Found {count} games" +} +``` + +Pluralisation: + +```json +{ + "n_games_found": "No games found | {count} game found | {count} games found" +} +``` + +Pipe-separated forms: zero | one | other. Not every language has three; `vue-i18n`'s pluralisation docs explain per-language rules. + +### What not to translate + +- Proper nouns (RomM, IGDB, ScreenScraper, RetroAchievements). +- Technical identifiers (env var names, config keys, URLs). +- Code samples and filenames. + +### RTL languages + +No RTL locales ship yet. The UI needs layout work before Arabic / Hebrew can present cleanly — if you want to contribute one, open a prep issue first so we can coordinate the layout fixes. + +## Testing your translation + +1. `cd frontend && npm install && npm run dev` +2. Open `http://localhost:3000`, pick your locale from Profile → User Interface → Language. +3. Click through every major screen — home dashboard, platform view, game detail, admin panel, settings. + +Look for: + +- **Clipped text.** Some translations are longer than their English originals; if a button breaks layout, flag it. +- **Grammatical weirdness with variables.** "Found 1 games" is wrong. Fix with pluralisation. +- **Fallback to English.** Shows up when a key is missing. Add it. + +## Tooling + +We don't use a translation-management platform (Weblate, Crowdin, etc.) in 5.0 — translations happen via PR. If interest grows, we may add one. Open an issue to discuss if you'd prefer it. + +## PR conventions + +- One PR per locale is ideal. +- Title: `i18n(): `. E.g. `i18n(fr_FR): fill in missing Admin panel strings`. +- Include screenshots of the changed screens — reviewers can't read every locale. +- Disclose AI assistance per [Contributing](contributing.md). + +## See also + +- [Languages](../using/languages.md) — the end-user view. +- [Contributing](contributing.md) — general contribution rules. +- [vue-i18n docs](https://vue-i18n.intlify.dev/) — upstream reference. diff --git a/docs/developers/index.md b/docs/developers/index.md index 6c836d9c..7b672b8c 100644 --- a/docs/developers/index.md +++ b/docs/developers/index.md @@ -1,10 +1,52 @@ --- -status: placeholder -wave: 1 +title: API & Development +description: Build on top of RomM — API reference, WebSockets, OpenAPI, local dev, contributing. --- # API & Development -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +For anyone **building something on top of or inside of RomM** — third-party apps, scripts, contributions to the core, translations. + +Looking for end-user or operator content? See [Using RomM](../using/index.md) or [Administration](../administration/index.md). + +## Working with the API + +- **[API Reference](api-reference.md)** — every REST endpoint. OpenAPI-driven. +- **[API Authentication](api-authentication.md)** — all five auth modes (session, Basic, OAuth2, Client API Token, OIDC). +- **[Consuming OpenAPI](openapi.md)** — codegen, Postman imports, schema validation. +- **[WebSockets](websockets.md)** — Socket.IO endpoints for live updates and Netplay. + +## Building companion apps + +- **[Client API Tokens](../ecosystem/client-api-tokens.md)** — how companion apps authenticate, including the device-pairing flow. +- **[Device Sync Protocol](../ecosystem/device-sync-protocol.md)** — wire-level reference for save/state/play-session sync. +- **[Argosy](../ecosystem/argosy.md), [Grout](../ecosystem/grout.md)** — reference implementations. + +## Contributing to RomM itself + +- **[Development Setup](development-setup.md)** — get a local environment running. +- **[Architecture](architecture.md)** — high-level walkthrough of the codebase. +- **[Contributing](contributing.md)** — process, style, AI-assistance disclosure. +- **[Translations (i18n)](i18n.md)** — add or improve a locale. +- **[Releasing](releasing.md)** — maintainer-only; how releases are cut. + +## Reference + +- **[Environment Variables](../reference/environment-variables.md)** — every env var. +- **[Configuration File](../reference/configuration-file.md)** — `config.yml` schema. +- **[Scheduled Tasks](../reference/scheduled-tasks.md)** — background job reference. +- **[Exports](../reference/exports.md)** — gamelist.xml / Pegasus export formats. +- **[Feeds](../reference/feeds.md)** — every URL-feed endpoint (Tinfoil, pkgj, WebRcade, etc.). +- **[Glossary](../reference/glossary.md)** — canonical terminology. + +## Quick orientation + +| I want to… | Start here | +| --- | --- | +| Call the REST API from a script | [API Authentication](api-authentication.md) + [API Reference](api-reference.md) | +| Generate a client library | [Consuming OpenAPI](openapi.md) | +| Sync saves from a handheld | [Device Sync Protocol](../ecosystem/device-sync-protocol.md) | +| Listen to scan events | [WebSockets](websockets.md) | +| Fix a bug in RomM | [Development Setup](development-setup.md) + [Contributing](contributing.md) | +| Translate the app | [Translations (i18n)](i18n.md) | +| Understand how it's put together | [Architecture](architecture.md) | diff --git a/docs/developers/openapi.md b/docs/developers/openapi.md index 1a27157f..baa2969b 100644 --- a/docs/developers/openapi.md +++ b/docs/developers/openapi.md @@ -1,10 +1,101 @@ --- -status: placeholder -wave: 3 +title: Consuming OpenAPI +description: Use RomM's OpenAPI spec for code generation, Postman imports, and API exploration. --- # Consuming OpenAPI -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +RomM ships its entire REST API as an OpenAPI 3.0 specification. That's the source of truth — every endpoint, every request/response schema, every parameter. Use it to generate clients, drive test suites, or import into tools. + +## Where to find the spec + +Every RomM instance serves: + +```text +{romm_url}/openapi.json +``` + +No authentication required — the spec itself is public, though calling most of the endpoints it describes requires auth. + +Human-readable versions: + +- **Swagger UI** at `{romm_url}/api/docs` +- **ReDoc** at `{romm_url}/api/redoc` + +Both are auto-generated from the same `openapi.json`. Swagger UI has a "Try it out" feature for live testing; ReDoc has a cleaner reading layout. + +## Versioning + +The spec is versioned alongside RomM. Every release tag includes a matching `openapi.json` in the release artifacts on GitHub. Snapshot the spec at a specific version if your client needs reproducible builds: + +```sh +curl https://romm.example.com/openapi.json > openapi-5.0.0.json +``` + +## Rendered API reference + +RomM's docs site embeds a rendered version of the spec at [API Reference](api-reference.md). It's built at docs-publish time from a pinned `openapi-v5.json` snapshot. If the live docs are behind the latest API, check the [API Reference](api-reference.md) page for the version it was built against. + +## Generating client libraries + +[`openapi-generator-cli`](https://openapi-generator.tech/) generates clients in 30+ languages. Example for a Python client: + +```sh +npx @openapitools/openapi-generator-cli generate \ + -i https://romm.example.com/openapi.json \ + -g python \ + -o ./romm-client-python +``` + +For TypeScript: + +```sh +npx @openapitools/openapi-generator-cli generate \ + -i https://romm.example.com/openapi.json \ + -g typescript-axios \ + -o ./romm-client-ts +``` + +Generated clients handle auth, request shaping, and response parsing. Drop in, import, call. Quality varies by generator target — Python and TypeScript are the best-tested. + +### Tips + +- **Pin the spec**, don't fetch live. Builds should be reproducible. +- **Regenerate on new RomM releases.** Breaking changes are rare but possible. +- **Patch if needed.** Generated clients sometimes need tweaks — upstream generator bugs, our spec's rough edges, etc. Keep a patch file. + +## Postman / Insomnia / Bruno + +All three import OpenAPI directly: + +- **Postman** — File → Import → paste `openapi.json` URL. +- **Insomnia** — Create → Import From → URL. +- **Bruno** — Import Collection → OpenAPI. + +Useful for manual API exploration during development. + +## Validating requests + +If you're building something that calls RomM, consider validating requests against the spec before sending. Schema-driven validation catches bugs early: + +- **Python** — [`openapi-core`](https://github.com/python-openapi/openapi-core). +- **Node.js** — [`openapi-backend`](https://github.com/anttiviljami/openapi-backend) or `ajv`-based approaches. +- **Go** — [`kin-openapi`](https://github.com/getkin/kin-openapi). + +## Spec quirks + +A few known quirks to work around: + +- **Some `additionalProperties` are loose.** RomM's spec lets some responses include fields not in the schema (debug hooks, feature-flag-gated fields). Don't treat the spec as an exact response guarantee — treat it as "everything here is always present; more may follow". +- **Socket.IO isn't in the OpenAPI spec.** WebSocket endpoints are documented separately in [WebSockets](websockets.md). +- **Pagination defaults vary per endpoint.** Some paginate, some don't. Check the spec per endpoint. + +## Webhooks & events + +Not currently in the spec. Event-driven integration is via [WebSockets](websockets.md) only. + +## See also + +- [API Reference](api-reference.md) — the pre-rendered version of the spec for browsing. +- [API Authentication](api-authentication.md) — required auth for most endpoints. +- [OpenAPI Initiative](https://www.openapis.org/) — upstream specification. diff --git a/docs/developers/releasing.md b/docs/developers/releasing.md index 7953f9fa..61be84af 100644 --- a/docs/developers/releasing.md +++ b/docs/developers/releasing.md @@ -1,10 +1,167 @@ --- -status: placeholder -wave: 3 +title: Releasing +description: How RomM releases are cut, published, and documented. Maintainer reference. --- # Releasing -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +Maintainer reference. If you're not cutting a RomM release, you don't need this page — see [Release Notes & Migration](../releases/index.md) for user-facing release info. + +## Release cadence + +RomM releases on a loose cadence — not scheduled, driven by readiness: + +- **Patch (`5.0.1`, `5.0.2`)** — bug fixes. Cut as needed; typically 1-4 per month. +- **Minor (`5.1.0`, `5.2.0`)** — additive features. Cut when a cohesive batch of features is stable. +- **Major (`6.0.0`)** — breaking changes. Planned well in advance, announced in the Discord + on GitHub. + +## Version numbering + +[SemVer](https://semver.org/) for breaking-change semantics: + +- **MAJOR** — backwards-incompatible schema change, env var rename, or API-contract break. +- **MINOR** — new feature, backwards-compatible. +- **PATCH** — bug fix only. + +Alembic migrations run on every startup; migrations are backwards-compatible within a major version. + +## Pre-release checklist + +### 1. Merge pending PRs + +- All release-milestoned PRs merged into `master`. +- CI green on `master`. +- Linter green: `trunk check --all`. +- Tests green: `uv run pytest`. + +### 2. Update version numbers + +- `pyproject.toml` → `version = "X.Y.Z"`. +- `frontend/package.json` → `"version": "X.Y.Z"`. +- Any hardcoded version strings (`backend/__init__.py`, etc.) — `rg '__version__'` or `rg '5\.0\.0'` to find them. + +### 3. Update `env.template` if needed + +If the release adds / renames / removes env vars, `env.template` is the canonical reference. Add/rename/remove lines with inline comments. Keep alphabetical order per section. + +The docs site auto-generates [Environment Variables](../reference/environment-variables.md) from `env.template` on every release — skip this step and the docs drift. + +### 4. Update changelog + +`CHANGELOG.md` at repo root (if present) — add a new section for the new version: + +```md +## 5.0.0 — 2026-04-18 + +### Breaking + +- ... + +### Added + +- Feature foo (#1234) +- ... + +### Fixed + +- Bug bar (#1235) +``` + +Link to the PRs / issues. This feeds into the GitHub Release notes. + +### 5. Tag and push + +```sh +git checkout master +git pull +git tag -a 5.0.0 -m "Release 5.0.0" +git push origin 5.0.0 +``` + +The tag triggers the Docker build workflow — `rommapp/romm:5.0.0` (full) and `rommapp/romm:5.0.0-slim` are built and published to Docker Hub + GHCR. + +### 6. Publish the GitHub Release + +From the GitHub UI or `gh` CLI: + +```sh +gh release create 5.0.0 \ + --title "RomM 5.0.0" \ + --notes-file release-notes.md \ + --latest +``` + +Release notes come from the `CHANGELOG.md` entry. For major releases, add a migration-guide link to the top of the notes. + +### 7. Move the `latest` and major tags + +The Docker workflow handles `5.0.0` and `5.0.0-slim` automatically. For `:latest` and `:5`: + +```sh +# only for non-prerelease, non-LTS-old-major +docker pull rommapp/romm:5.0.0 +docker tag rommapp/romm:5.0.0 rommapp/romm:latest +docker tag rommapp/romm:5.0.0 rommapp/romm:5 +docker push rommapp/romm:latest +docker push rommapp/romm:5 +``` + +(Normally automated in the release workflow; manual fallback above.) + +## Announcements + +- **Discord `#announcements`** — post a short summary with a link to the GitHub Release. +- **Reddit** (r/selfhosted, etc.) — optional, for major versions. +- **Docs site version switcher** — publish a new `mike`-managed version of the docs. See [Docs versioning](#docs-versioning). + +## Docs versioning + +RomM's docs are versioned with [`mike`](https://github.com/jimporter/mike). After a RomM release: + +```sh +# in rommapp/docs repo +uv run mike deploy --push --update-aliases 5.0 latest +``` + +This publishes `/5.0/` on the docs site and moves the `latest` alias to `5.0`. Older versions stay at their historical URL (e.g. `/4.8/`). + +See the [Versioning section](../releases/index.md#docs-versions) in Release Notes for the full context. + +## Post-release + +### Patch if needed + +If a regression ships in the release: + +1. Cherry-pick the fix into a `5.0.x` branch. +2. Bump to `5.0.1` in `pyproject.toml` + `package.json`. +3. Tag and release. +4. Move `:latest` to the new patch. + +### Track issues + +Post-release, expect a spike in issues. Triage Day-1 issues aggressively — breakage reports need immediate attention; nice-to-haves can wait. + +## Security releases + +For security fixes: + +1. Don't disclose in the commit message. Use "fix" generically. +2. Cut the release. +3. **After** the release is live, publish a GitHub Security Advisory with the CVE, timeline, and affected versions. +4. Announce in Discord with a short summary and an "update now" recommendation. + +## Breaking-change protocol + +For major versions: + +1. **Announce early.** Open a tracking issue at least a month out. Post to Discord. +2. **Document the migration.** Write the upgrade guide *before* the release, so it's ready at cut time. +3. **Deprecate first when possible.** Ship a warning in a minor release before removing in a major. +4. **Migration scripts.** If the breaking change requires user action, ship a script in `scripts/migrate_.py` or similar. + +## See also + +- [Release Notes & Migration](../releases/index.md) — user-facing side. +- [Upgrading to 5.0](../releases/upgrading-to-5.0.md) — reference migration guide style. +- [Contributing](contributing.md) — general contribution process. diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md index 49fe094a..b9b16ea2 100644 --- a/docs/developers/websockets.md +++ b/docs/developers/websockets.md @@ -1,10 +1,148 @@ --- -status: placeholder -wave: 3 +title: WebSockets +description: RomM's two Socket.IO endpoints — live updates and Netplay coordination. --- # WebSockets -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +RomM uses **Socket.IO** for real-time communication. Two endpoints: + +| Endpoint | Purpose | +| --- | --- | +| `/ws/socket.io` | General live updates — scan progress, task status, task completion, admin notifications. | +| `/netplay/socket.io` | Netplay session coordination — room discovery, join/leave events, session lifecycle. | + +Both are Redis-backed (via `socket.io-redis`) so multi-instance RomM deployments broadcast events to every replica. + +## Why Socket.IO not raw WebSocket? + +RomM inherited Socket.IO from the Vue frontend, which uses `socket.io-client`. Sticking with Socket.IO avoids protocol drift, works in every browser, and handles reconnection + message framing for us. + +If you're writing a non-browser client in a language that has a Socket.IO library (Python's `python-socketio`, Go's `go-socket.io`, etc.), the protocol is straightforward. Raw WebSocket without Socket.IO framing **will not** work — Socket.IO adds its own handshake and message envelope. + +## Authentication + +Socket.IO connections inherit the HTTP session — if your `Cookie` header carries a RomM session cookie, the WS connection is authenticated as that user. Programmatic clients should pass the session or token via the handshake `auth` field: + +```javascript +const socket = io("https://romm.example.com", { + path: "/ws/socket.io", + auth: { + token: "rmm_..." + } +}); +``` + +If auth fails, the connection is closed with an error event. + +## `/ws/socket.io` — general events + +### Namespaces + +Default namespace. No sub-namespacing in 5.0. + +### Server → client events + +| Event | Payload | When | +| --- | --- | --- | +| `scan:start` | `{ scan_id, platforms, mode }` | A scan starts. | +| `scan:log` | `{ scan_id, level, message }` | Live scan log line. | +| `scan:progress` | `{ scan_id, platform, matched, unmatched, total }` | Per-platform progress update. | +| `scan:complete` | `{ scan_id, summary }` | Scan finished. | +| `task:start` | `{ task_id, task_name }` | Any scheduled/manual task starts. | +| `task:complete` | `{ task_id, task_name, status }` | Task finished (status: success/failed). | +| `rom:created` | `{ rom_id }` | New ROM added. | +| `rom:updated` | `{ rom_id }` | Metadata or user data changed. | +| `rom:deleted` | `{ rom_id }` | ROM removed. | +| `notification` | `{ message, level }` | Admin broadcast / error message. | + +### Client → server events + +No state changes via WebSocket — RomM's design is "REST for writes, Socket.IO for reads". A client can: + +- Emit `subscribe:scan` with a scan ID to join that scan's broadcast group. +- Emit `unsubscribe:scan` to leave. + +Actual scan / task / ROM operations happen via the REST API — see [API Reference](api-reference.md). + +## `/netplay/socket.io` — Netplay coordination + +Separate endpoint for Netplay rooms. Used by EmulatorJS's Netplay logic; rarely touched directly. + +### Server → client events + +| Event | Payload | +| --- | --- | +| `netplay:room_created` | `{ room_id, host, rom_id, password_protected }` | +| `netplay:room_updated` | `{ room_id, players }` | +| `netplay:room_destroyed` | `{ room_id }` | + +### Client → server events + +| Event | Payload | +| --- | --- | +| `netplay:create_room` | `{ rom_id, password?, max_players }` | +| `netplay:join_room` | `{ room_id, password? }` | +| `netplay:leave_room` | `{ room_id }` | + +Plus WebRTC signalling events (`offer`, `answer`, `ice_candidate`) that shuttle between peers for the actual game session. These are opaque to anything but EmulatorJS. + +## Redis backend (multi-instance) + +When you run multiple RomM replicas behind a load balancer, a WS-originated event on one replica needs to reach a client connected to another replica. `socket.io-redis` handles that — events are published to a Redis pub/sub channel, every replica subscribes, every replica delivers to its local sticky-session clients. + +Required config when running multi-replica: + +- `REDIS_HOST` + `REDIS_PORT` shared across replicas. +- Load balancer with **sticky sessions** (client IP or cookie hash). + +Without sticky sessions, Socket.IO's handshake polling phase can bounce between replicas and fail — see [Socket.IO multi-server docs](https://socket.io/docs/v4/using-multiple-nodes/) for context. + +## Reverse-proxy requirements + +Every reverse-proxy setup must forward the WebSocket upgrade. See [Reverse Proxy](../install/reverse-proxy.md) — all recipes there keep WebSockets on. + +Common breakages: + +- Nginx without `proxy_set_header Upgrade $http_upgrade` / `Connection "upgrade"`. +- Cloudflare with WebSockets off in Network settings. +- Traefik without the default passthrough middlewares. + +Symptom of a broken WS: HTTP 400 responses on the upgrade, and the browser console full of `WebSocket connection failed`. See [Authentication Troubleshooting → WebSockets](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). + +## Writing a client in Python + +Simple example — tail scan logs: + +```python +import socketio + +sio = socketio.Client() + +@sio.event +def connect(): + print("connected") + +@sio.on("scan:log") +def scan_log(data): + print(f"[{data['level']}] {data['message']}") + +sio.connect( + "https://romm.example.com", + socketio_path="/ws/socket.io", + auth={"token": "rmm_..."}, +) +sio.wait() +``` + +## Limitations + +- **Not every backend action emits a WS event** — only the ones listed above. If you need a specific event, open an issue. +- **No room-based user presence yet** — "who else is online" isn't exposed. +- **Netplay WebRTC is peer-to-peer after initial handshake** — RomM only brokers; the actual gameplay data never touches RomM's servers. + +## See also + +- [API Authentication](api-authentication.md) — general auth primer. +- [Reverse Proxy](../install/reverse-proxy.md) — every recipe needs WebSocket passthrough. +- [Netplay](../using/netplay.md) — end-user-facing side of the `/netplay` endpoint. From 7c5e637572cddbf6d9d62a1f7573e8195d9f7583 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 18:38:34 +0000 Subject: [PATCH 019/121] docs: finish Wave 3 reference + about sections (8 pages) Completes Wave 3 minus the still-blocked developers/api-reference.md. Reference (4 new pages): - reference/exports.md: gamelist.xml + Pegasus export formats with config.yml toggles, format examples, API triggers, ES-DE settings for round-trip compatibility - reference/feeds.md: catalogue of every URL feed endpoint (Tinfoil, pkgi psvita/psp games/dlc, fpkgi ps4/ps5, Kekatsu, WebRcade, legacy pkgj) with auth behaviour, response formats, extension filtering, platform-slug conventions - reference/ports-and-endpoints.md: container-port table (8080 nginx, 5000 gunicorn internal, 6379 redis internal), URL-path taxonomy (unauthenticated / session / token / download-with-bypass / feeds / websockets), outbound destinations, ROMM_BASE_PATH path-prefix usage - reference/glossary.md: alphabetised canonical-term dictionary (~60 entries). Pairs with the pedagogical getting-started/ concepts.md About (4 new pages): - about/faqs.md: real FAQ (replaces the 18-line joke version). What is RomM, licensing, comparisons, deployment, resource reqs, updates, scan/platform troubleshooting, sharing, backup, offline use - about/brand-guidelines.md: ported from Miscellaneous/. Logo palette, do/don't lists, downloadable asset pointers - about/license.md: AGPL-3.0 on core + per-repo license table + third-party components + FAQ about commercial/SaaS use + contributor agreement - about/credits.md: core team pointer, contributors, translators, community apps, upstream projects (in-browser emulation + metadata sources + backend stack + frontend stack + docs stack), financial supporters via Open Collective Builds clean with --strict (after fixing one anchor slug typo). --- docs/about/brand-guidelines.md | 84 ++++++++++++++- docs/about/credits.md | 102 +++++++++++++++++- docs/about/faqs.md | 146 ++++++++++++++++++++++++- docs/about/license.md | 80 +++++++++++++- docs/reference/exports.md | 148 +++++++++++++++++++++++++- docs/reference/feeds.md | 109 ++++++++++++++++++- docs/reference/glossary.md | 142 +++++++++++++++++++++++- docs/reference/ports-and-endpoints.md | 133 ++++++++++++++++++++++- 8 files changed, 904 insertions(+), 40 deletions(-) diff --git a/docs/about/brand-guidelines.md b/docs/about/brand-guidelines.md index 9a271824..80a5ae5e 100644 --- a/docs/about/brand-guidelines.md +++ b/docs/about/brand-guidelines.md @@ -1,10 +1,84 @@ --- -status: placeholder -wave: 3 +title: Brand Guidelines +description: How to use the RomM name, logo, and colors. --- + + # Brand Guidelines -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +Guidelines for anyone who wants to use the RomM name and logo. In this context, "RomM", "The RomM Project", "the project", "we", "us", and "our" refer to the RomM project. + +## The logo + +
+ RomM logo +
+
+
+ +The logo should always be used in its standard colors: + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColorHex
#371f69#371f69
#553e98#553e98
#ede5f8#ede5f8
#bea4e1#bea4e1
#e6c7a7#e6c7a7
#e1a38d#e1a38d
+
+ +![Branding guidelines poster](../resources/romm/brand_guidelines/poster.png) + +## Do these things + +- Use our logo to link to any page or site operated by the project. +- Use our logo in a blog post or news article about the project. +- Use our logo to inform others that your project integrates with RomM. +- Always use our logo in the colors provided. +- Always use our name in a way that makes clear you are not affiliated with the project. + +If you're building something that integrates with RomM and would like to use / remix the logo, **please reach out first** via [Discord](https://discord.gg/romm). We'd love to hear about it. + +## Please don't + +- Use our name or logo in any way that suggests you are us, are endorsed by us, or are part of the project. +- Use our name or logo in a way that implies partnership, sponsorship, or endorsement. +- Use our name or logo as the name or logo for your project, product, service, social media account, company, or website. +- Use our name or logo to promote, advertise, or sell any private business, closed-source software, commercial product, or paid service. + +## Downloadables + +The logo assets live at [rommapp/romm/tree/master/frontend/assets/](https://github.com/rommapp/romm/tree/master/frontend/assets/): + +- `isotipo.svg` / `isotipo.png` — the mark (circular logo). +- `logotipo.svg` / `logotipo.png` — the wordmark. +- `social_preview.png` — GitHub social preview. + +## Questions + +Ask on [Discord](https://discord.gg/romm) or open an issue. diff --git a/docs/about/credits.md b/docs/about/credits.md index b6d7bea4..298df4a9 100644 --- a/docs/about/credits.md +++ b/docs/about/credits.md @@ -1,10 +1,102 @@ --- -status: placeholder -wave: 3 +title: Credits +description: The humans, projects, and services that make RomM possible. --- # Credits -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +RomM exists because a lot of people contributed code, designs, translations, ideas, and — most importantly — running bug reports back to the project. Thanks to every one of them. + +## Core maintainers + +The small team that reviews PRs, cuts releases, and keeps the project moving. Check [rommapp/romm](https://github.com/rommapp/romm/graphs/contributors) for the current active list — credits here would drift. + +## Contributors + +[Every contributor](https://github.com/rommapp/romm/graphs/contributors) to [rommapp/romm](https://github.com/rommapp/romm), plus contributors to the surrounding repos: + +- [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher/graphs/contributors) +- [rommapp/grout](https://github.com/rommapp/grout/graphs/contributors) +- [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin/graphs/contributors) +- [rommapp/muos-app](https://github.com/rommapp/muos-app/graphs/contributors) +- [rommapp/docs](https://github.com/rommapp/docs/graphs/contributors) + +## Translators + +RomM's 19 locales exist because individual community members took the time to translate the UI. See each locale folder's commit history at [rommapp/romm/tree/master/frontend/src/locales](https://github.com/rommapp/romm/tree/master/frontend/src/locales) for per-locale credit. + +## Community apps + +Built by the community, not the RomM team. Full list in [Community Apps](../ecosystem/community-apps.md). + +## Upstream projects + +RomM stands on an enormous amount of open-source work. In rough order of "how visible they are to users": + +### In-browser emulation + +- [EmulatorJS](https://emulatorjs.org/) — the retro emulator that powers most of our in-browser play. +- [Ruffle](https://ruffle.rs/) — the Flash / Shockwave emulator. +- [dosbox-pure](https://github.com/schellingb/dosbox-pure) — DOS emulation core via EmulatorJS. + +### Metadata sources + +- [IGDB](https://www.igdb.com/) — the Internet Game Database. +- [ScreenScraper](https://screenscraper.fr/) — French community metadata. +- [MobyGames](https://www.mobygames.com/) — game database. +- [RetroAchievements](https://retroachievements.org/) — achievements and hash matching. +- [SteamGridDB](https://www.steamgriddb.com/) — cover art. +- [Hasheous](https://hasheous.org/) — hash-based matching. +- [PlayMatch](https://github.com/RetroRealm/playmatch) — community hash service. +- [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) — local metadata DB. +- [TheGamesDB](https://thegamesdb.net/) — free community DB. +- [Flashpoint Archive](https://flashpointproject.github.io/flashpoint-database/) — Flash game preservation. +- [HowLongToBeat](https://howlongtobeat.com/) — completion times. +- [Libretro](https://www.libretro.com/) — core metadata. + +### Backend stack + +- [FastAPI](https://fastapi.tiangolo.com/) + [Starlette](https://www.starlette.io/) — the web framework. +- [SQLAlchemy](https://www.sqlalchemy.org/) + [Alembic](https://alembic.sqlalchemy.org/) — ORM and migrations. +- [RQ](https://python-rq.org/) — background job queue. +- [MariaDB](https://mariadb.org/) / [PostgreSQL](https://www.postgresql.org/) / [MySQL](https://www.mysql.com/) — database backends. +- [Redis](https://redis.io/) / [Valkey](https://valkey.io/) — cache and queue. +- [nginx](https://nginx.org/) with [`mod_zip`](https://github.com/evanmiller/mod_zip) — reverse proxy + streaming zip downloads. +- [uv](https://docs.astral.sh/uv/) — Python package manager. + +### Frontend stack + +- [Vue 3](https://vuejs.org/) — frontend framework. +- [Vuetify](https://vuetifyjs.com/) — component library. +- [Pinia](https://pinia.vuejs.org/) — state management. +- [Vite](https://vitejs.dev/) — build tool. +- [Socket.IO](https://socket.io/) — real-time communication. +- [vue-i18n](https://vue-i18n.intlify.dev/) — localisation. +- [rom-patcher-js](https://www.marcrobledo.com/RomPatcher.js/) — the ROM patcher library. +- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) — PWA support. + +### Docs stack + +- [MkDocs](https://www.mkdocs.org/) + [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) — what you're reading. +- [mike](https://github.com/jimporter/mike) — docs versioning. +- [asciinema](https://asciinema.org/) — terminal recordings. + +## Community + +- [#selfh.st](https://selfh.st/) — early visibility for the project. +- [Hacker News](https://news.ycombinator.com/) — the launch-day bump. +- [Aikido Security](https://www.aikido.dev/) — security audit. +- Everyone who submitted a bug report, pinged Discord, or pushed a typo fix. + +## Financial supporters + +Donors via [Open Collective](https://opencollective.com/romm) make continued development possible. The project wouldn't exist without you. Thanks. + +## Missing? + +Open a PR against this page. Credit is cheap; we'd rather err on the side of naming everyone than leaving someone out. + +## See also + +- [License](license.md) — the legal bit. +- [Contributing](../developers/contributing.md) — if you want to be on this list. diff --git a/docs/about/faqs.md b/docs/about/faqs.md index fd1e139f..10171682 100644 --- a/docs/about/faqs.md +++ b/docs/about/faqs.md @@ -1,10 +1,146 @@ --- -status: placeholder -wave: 3 +title: FAQs +description: Answers to the questions users most often ask about RomM. --- # FAQs -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +## What is RomM? + +A self-hosted ROM manager + player. Scan your library, get metadata, browse a clean UI, play in the browser, sync to handhelds, run it on your own hardware. + +See [Introduction](../index.md) for the full pitch. + +## Is it free? + +Yes. [AGPL-3.0](license.md). Core always will be free; other repos in the umbrella use permissive licenses. No tracking, no upsells. + +## How does it compare to [X other manager]? + +Not a direct comparison page. Short version: RomM emphasises self-hosted + multi-user + in-browser-play + companion-app ecosystem. If those matter, try RomM. If you just want a local Windows app that scans a folder, tools like LaunchBox may fit better. + +## Do I need metadata API keys? + +Not strictly. RomM runs without any — games just won't match to a metadata source, so no covers, descriptions, or ratings. + +Recommended: IGDB + ScreenScraper. See [Metadata Providers](../administration/metadata-providers.md) for the full list. + +## Is RomM legal? + +The software is legal. What you put in it depends on your jurisdiction. We don't ship ROMs or firmware, don't help you find them, and can't give legal advice. General rule: dumping games you own is usually fine, distributing copies is usually not. + +## Can I run RomM on [X]? + +Probably. Supported deployment paths: + +- Docker Compose (Linux, macOS, Windows with WSL). +- Unraid / Synology / TrueNAS SCALE. +- Kubernetes. + +See [Install & Deploy](../install/index.md). + +Not supported: + +- Bare metal without containers (not documented; may work). +- TrueNAS CORE (FreeBSD; no Docker). +- Windows without WSL (Docker Desktop works; bare Windows doesn't). + +## How much RAM / CPU does it need? + +- **Minimum** (small library, 1 user): 512 MB RAM, any modest CPU. +- **Comfortable** (thousands of ROMs, a few users, occasional scans): 2 GB RAM, 2 cores. +- **In-browser play**: browser-side resource-heavy, RomM-server-side negligible. + +Heaviest CPU is during scans — hashing + network-bound metadata calls. Plan for spikes. + +## How do I update? + +```sh +docker compose pull +docker compose up -d +``` + +Alembic migrations run automatically. Read the release notes before major versions. See [Release Notes & Migration](../releases/index.md). + +## Why can't I see a specific platform? + +Platform folder name probably doesn't match a known slug. Check [Supported Platforms](../platforms/supported-platforms.md). Fix with either folder rename or a [`system.platforms`](../reference/configuration-file.md#systemplatforms) binding in `config.yml`. + +## Why are my ROMs unmatched after a scan? + +Most common reasons: + +- No metadata providers configured — enable at least one. +- Filename too generic (no tags, unusual naming) — add filename tags like `(igdb-1234)` or try another provider. +- Wrong platform detection — see previous FAQ. + +Full troubleshooting: [Scanning Troubleshooting](../troubleshooting/scanning.md). + +## Can I share my library with friends? + +Yes — add them as users via the invite flow, then either share the URL (if accessible to them) or put RomM behind a VPN / Tailscale. See [Invitations & Registration](../administration/invitations-and-registration.md) + [Mobile & TV → Self-hosting tips](../using/mobile-and-tv.md#self-hosting-tips). + +## Can guests browse without an account? + +Yes — set `KIOSK_MODE=true`. Anonymous visitors get read-only access. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). + +## How do I back up? + +`mysqldump` the DB + rsync the `/romm/assets` and `/romm/config` volumes nightly. Full procedure + test-restore protocol in [Backup & Restore](../install/backup-and-restore.md). + +## Can I use RomM without the internet? + +Mostly. You need internet for: + +- First-time scan with metadata providers (they're online APIs). +- Pulling the Docker image on install or upgrade. +- OIDC login (if you use a cloud IdP). + +After the initial setup, browsing and playing can work offline. In-browser play downloads the emulator bundle on first launch, then caches it. + +## Why is scan X slow? + +Several possibilities, in rough order of likelihood: + +1. Hashing large files on spinning disks. +2. Metadata providers rate-limiting (mostly ScreenScraper). +3. Many files on a network mount with high latency. + +Full troubleshooting: [Scanning Troubleshooting → Hash calculations are slow](../troubleshooting/scanning.md#hash-calculations-are-slow). + +## When will [feature X] be added? + +We don't give ETAs on individual features. Open (or upvote) an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) to make the interest visible. + +For public roadmap-ish information, watch the GitHub Projects board and `#announcements` on Discord. + +## What happened to SQLite? + +Dropped in 3.0 for stability reasons. Use MariaDB, MySQL, or Postgres instead. See [Databases](../install/databases.md). + +## I found a bug. + +Open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) with: + +- RomM version. +- Deployment (Docker Compose / Unraid / K8s / etc.). +- Exact repro steps. +- Relevant logs — redact any secrets. + +## Who runs RomM? + +A small team of maintainers plus a chunk of active community contributors. AGPL-licensed, community-driven. Support the project via [Open Collective](https://opencollective.com/romm) if you'd like. + +## Where's Discord / GitHub / the website? + +- **Discord**: [discord.gg/romm](https://discord.gg/romm) +- **GitHub**: [rommapp/romm](https://github.com/rommapp/romm) +- **Website**: [romm.app](https://romm.app/) +- **Demo**: [demo.romm.app](https://demo.romm.app/) +- **Docs**: here ([docs.romm.app](https://docs.romm.app/)) + +## See also + +- [Core Concepts](../getting-started/concepts.md) — if the vocabulary is new. +- [Troubleshooting](../troubleshooting/index.md) — if something's broken. +- [Release Notes](../releases/index.md) — for version-specific questions. diff --git a/docs/about/license.md b/docs/about/license.md index e2f79dec..94eb6d91 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -1,10 +1,80 @@ --- -status: placeholder -wave: 3 +title: License +description: How RomM is licensed, and what that means for you. --- # License -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +## Core app — AGPL-3.0 + +The main [RomM application](https://github.com/rommapp/romm) is licensed under the [GNU Affero General Public License v3.0](https://choosealicense.com/licenses/agpl-3.0/). + +In short, AGPL says: + +- **You can** use RomM privately, commercially, or at any scale. +- **You can** modify the source code however you want. +- **You must** make your modifications available under AGPL-3.0 if you distribute them *or* run them as a network-accessible service for others. +- **You must** preserve copyright and license notices. + +The "network service" clause is the key AGPL twist — if you host a modified RomM and other people use it over the network, you owe them the source. This prevents the "hosted SaaS fork without upstream contributions" failure mode that's common with plain GPL. + +For most self-hosters this has zero practical effect — you're running an unmodified version for yourself, the license doesn't constrain you. For companies thinking about shipping a modified RomM commercially, understand the AGPL obligations first. + +Full license text: [rommapp/romm/blob/master/LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE). + +## Companion apps — varies + +The RomM umbrella hosts several projects under different licenses: + +| Project | License | +| --- | --- | +| [rommapp/romm](https://github.com/rommapp/romm) | AGPL-3.0 | +| [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher) | GPL-3.0 | +| [rommapp/grout](https://github.com/rommapp/grout) | MIT | +| [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin) | GPL-3.0 | +| [rommapp/muos-app](https://github.com/rommapp/muos-app) | check repo | +| [rommapp/docs](https://github.com/rommapp/docs) (what you're reading) | CC0 | + +Companion repos use more permissive licenses (GPL-3.0 or MIT) because they're smaller, more-replaceable, and don't host the library — the AGPL network-service clause doesn't offer the same protection benefits there. + +Docs (this site) are CC0 — do whatever you want with the content; attribution appreciated but not required. + +## Third-party components + +RomM ships several third-party components with their own licenses — [EmulatorJS](https://emulatorjs.org/), [Ruffle](https://ruffle.rs/), Vue, FastAPI, and a long list of smaller dependencies. Their licenses apply to their respective code; none of them override AGPL-3.0 on the RomM code itself. + +Full list via `uv tree` in the backend and `npm ls` in the frontend. Redistribution respects each upstream's terms. + +## FAQ + +### Can I use RomM at work / for my company's gaming night? + +Yes. AGPL doesn't restrict private or commercial use. + +### Can I fork RomM and relicense my fork? + +No. AGPL is a strong copyleft — forks remain AGPL. You can add your own changes under AGPL, but can't relicense the original code. + +### Can I charge money for RomM-as-a-service? + +Yes, but you owe your users the source. You can't run a modified closed-source hosted RomM for paying customers. + +### Can I sell ROMs through RomM? + +RomM doesn't care. Legality of doing so is governed by copyright law, not AGPL. + +### Is there a commercial / dual-license option? + +No. AGPL-only. If that's a blocker, RomM isn't the right choice. + +## Contributions + +By contributing code to RomM, you agree your contribution is licensed under the same AGPL-3.0 (or, for companion repos, the repo's license). + +Full contributor terms: [Contributing → Licensing](../developers/contributing.md#licensing). + +## See also + +- [Credits](credits.md) — the humans and projects behind RomM. +- [AGPL-3.0 overview](https://choosealicense.com/licenses/agpl-3.0/) — plain-language explainer. +- Full license text: [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE). diff --git a/docs/reference/exports.md b/docs/reference/exports.md index c0a7f4ba..15c110ea 100644 --- a/docs/reference/exports.md +++ b/docs/reference/exports.md @@ -1,10 +1,148 @@ --- -status: placeholder -wave: 3 +title: Exports +description: Export RomM metadata for use in other frontends — gamelist.xml and Pegasus format. --- # Exports -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +RomM can emit your library metadata in formats other frontends expect. Useful when you run RomM as the library authority and a separate frontend (ES-DE, Batocera, Pegasus) as the actual launcher. + +## gamelist.xml (ES-DE / Batocera / RetroBAT) + +ES-DE, Batocera, and compatibles look for a `gamelist.xml` in each platform folder. RomM can generate these automatically. + +### Enable via `config.yml` + +```yaml +scan: + gamelist: + export: true + media: + thumbnail: box2d # which media type to use as thumbnail + image: screenshot # which as full image +``` + +With `export: true`, every scan writes a `gamelist.xml` into the platform folder, and downloads the selected media into sibling folders (`covers/`, `screenshots/`, etc.) that ES-DE expects. + +### Format + +Standard ES-DE / EmulationStation format: + +```xml + + + ./metroid.nes + Metroid + ... + 19861115T000000 + Nintendo R&D1 + Nintendo + Action-Adventure + ./screenshots/metroid.png + ./covers/metroid.png + + ... + +``` + +### API + +Trigger an export on demand: + +```http +POST /api/export/gamelist-xml +Authorization: Bearer +Content-Type: application/json + +{ + "platforms": ["snes", "nes"] // optional; empty = all +} +``` + +Response includes where the files were written. + +### Using with ES-DE + +Once RomM has generated `gamelist.xml` and populated `covers/` + `screenshots/`, point ES-DE at the library: + +```xml + + +``` + +- `MediaDirectory` — point it at the ROM folder (same path ES-DE uses for `ROMDirectory`), so ES-DE looks for media in-place rather than in its own library. +- `LegacyGamelistFileLocation` — makes ES-DE write updates back to the same `gamelist.xml` RomM reads from, rather than its separate config dir. + +See also [Metadata Providers → gamelist.xml](../administration/metadata-providers.md) for the *import* direction (reading gamelist.xml into RomM). + +## Pegasus + +[Pegasus](https://pegasus-frontend.org/) is an alternative gaming frontend with its own metadata format. RomM can emit a `metadata.pegasus.txt` per platform. + +### Enable via `config.yml` + +```yaml +scan: + pegasus: + export: true +``` + +### Format + +Human-readable text format: + +```text +collection: Super Nintendo +shortname: snes +launch: /usr/bin/snes9x "{file.path}" + +game: Super Metroid +file: /roms/snes/Super Metroid (USA).sfc +description: ... +developer: Nintendo R&D1 +publisher: Nintendo +genre: Action-Adventure +release: 1994-03-19 +assets.box: ./covers/Super Metroid.png +assets.screenshot: ./screenshots/Super Metroid.png +``` + +### API + +```http +POST /api/export/pegasus +Authorization: Bearer +Content-Type: application/json + +{ + "platforms": ["snes", "nes"] +} +``` + +## Other formats + +Not in 5.0. If you want another format (LaunchBox, Attract-Mode, etc.), open an issue with examples of the expected schema. + +## Re-running on changes + +Exports don't auto-rerun on every metadata edit. Triggers: + +- **Next scan** — exports are part of scan completion when enabled. +- **Manual trigger** via the API above. +- **Admin → Tasks → Export** (if surfaced in your build). + +For tight sync between RomM edits and the external frontend, run the export via cron or after major edits. + +## Hash stability + +Both export formats include some IDs (`igdb_id`, `moby_id`, etc.). If you rematch metadata, those IDs change. Don't point tools at the IDs as stable keys — use filenames / paths. + +## Permissions + +`tasks.run` scope required to trigger an export via API. Config-driven export runs as the scan does — no extra permission check. + +## See also + +- [Configuration File → `scan.gamelist`](../reference/configuration-file.md#scangamelistexport-new-in-50) +- [Configuration File → `scan.pegasus`](../reference/configuration-file.md#scanpegasusexport-new-in-50) +- [Metadata Providers → gamelist.xml importer](../administration/metadata-providers.md) — reverse direction. diff --git a/docs/reference/feeds.md b/docs/reference/feeds.md index e977bc4f..c34c70b5 100644 --- a/docs/reference/feeds.md +++ b/docs/reference/feeds.md @@ -1,10 +1,109 @@ --- -status: placeholder -wave: 3 +title: Feeds +description: Every URL-feed endpoint RomM exposes for third-party installers and frontends. --- # Feeds -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +RomM exposes URL feeds for several homebrew installers and frontends. Each feed is a JSON / CSV / XML representation of a filtered subset of your library in the format the target tool expects. + +Feeds are **read-only**. They expose download URLs back to RomM's own `/api/...` endpoints for the actual file transfer. + +## Endpoint catalogue + +| Feed | URL | Purpose | +| --- | --- | --- | +| **Tinfoil** | `/api/feeds/tinfoil` | Nintendo Switch `.nsp` / `.xci` installer. [Setup →](../ecosystem/tinfoil.md) | +| **pkgi (PS Vita games)** | `/api/feeds/pkgi/psvita/game` | PS Vita `.pkg` installer. [Setup →](../ecosystem/pkgj.md) | +| **pkgi (PS Vita DLCs)** | `/api/feeds/pkgi/psvita/dlc` | Same, DLC content. | +| **pkgi (PSP games)** | `/api/feeds/pkgi/psp/game` | PSP `.pkg` installer. | +| **pkgi (PSP DLCs)** | `/api/feeds/pkgi/psp/dlc` | Same, DLC content. | +| **fpkgi (PS4)** | `/api/feeds/fpkgi/ps4` | PS4 `.pkg` installer. [Setup →](../ecosystem/fpkgi.md) | +| **fpkgi (PS5)** | `/api/feeds/fpkgi/ps5` | PS5 `.pkg` installer. | +| **Kekatsu (NDS)** | `/api/feeds/kekatsu/nds` | Nintendo DS multiboot loader. [Setup →](../ecosystem/kekatsu.md) | +| **WebRcade** | `/api/feeds/webrcade` | Browser-based retro frontend. [Setup →](../ecosystem/webrcade.md) | + +Plus legacy `pkgj` formats for individual platforms: + +| Feed | URL | +| --- | --- | +| PKGj PSX | `/api/feeds/pkgj/psx` | +| PKGj PS Vita | `/api/feeds/pkgj/psvita` | +| PKGj PSP | `/api/feeds/pkgj/psp` | + +## Authentication + +All feeds respect `DISABLE_DOWNLOAD_ENDPOINT_AUTH`: + +- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=false`** (default, secure) — feeds require basic auth. Most clients (Tinfoil, pkgj, fpkgi) can send basic auth in their URL config. +- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`** — feeds are public. Use when RomM is behind upstream auth (reverse proxy with auth, VPN). + +See [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass) for the full security discussion. + +## Response formats + +### JSON feeds + +Most feeds return JSON: Tinfoil, fpkgi, WebRcade. + +```json +{ + "success": true, + "files": [ + { + "url": "https://romm.example.com/api/roms/1234/content/mario.nsp", + "title": "Super Mario Odyssey", + "size": 4500000000, + "titleid": "0100000000010000", + ... + } + ] +} +``` + +### CSV feeds + +pkgi uses CSV per upstream's format — one line per game: + +```csv +psvita,PCSA00003,Unknown,Game,1.0,https://romm.example.com/...,md5=...,52428800 +``` + +### Format per feed + +Each feed's exact schema matches what its client expects. Don't call feeds by accident from other tools — Tinfoil can't parse WebRcade JSON, and vice versa. + +## Filtering + +Most feeds filter by file extension automatically: + +- **Tinfoil** → `.nsp`, `.xci`, `.nsz`, `.xcz`. +- **pkgi** / **fpkgi** → `.pkg`. +- **Kekatsu** → `.nds`. +- **WebRcade** → any extension the WebRcade emulator supports. + +Games not matching the expected extension are silently skipped, not reported as missing. + +## Performance notes + +Feeds query the whole library for the target platform on every request. For large libraries that's measurable but not slow (< 1s typically). Clients generally cache feed responses — don't hit the feed in a tight loop. + +## Platform slugs + +The `{platform_slug}` in feed URLs (`fpkgi/{platform}`, `kekatsu/{platform}`) takes RomM's canonical platform slug. See [Supported Platforms](../platforms/supported-platforms.md) for the full list. + +If you've remapped your folder names to canonical slugs via [`system.platforms`](configuration-file.md#systemplatforms), use the canonical slug in the feed URL, not your folder name. + +## Adding a new feed format + +If you want RomM to expose a feed format it doesn't currently support: + +1. Check the [backend/routers/feeds/](https://github.com/rommapp/romm/tree/master/backend/routers) directory for existing implementations as templates. +2. Open an issue describing the target client, its feed format, and any authentication requirements. +3. Ideally, open a PR. Feed endpoints are usually small — a few dozen lines of Python to filter + format. + +## See also + +- [Ecosystem](../ecosystem/index.md) — per-client setup guides. +- [Authentication](../administration/authentication.md) — auth options affecting feeds. +- [API Reference](../developers/api-reference.md) — full endpoint catalogue. diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md index 22ec3cce..bdeb7954 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -1,10 +1,142 @@ --- -status: placeholder -wave: 3 +title: Glossary +description: Canonical RomM terminology in one alphabetised lookup. --- # Glossary -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +Every term the RomM docs, UI, and API use consistently. Alphabetical. + +For the pedagogical version with reasoning and examples, see [Core Concepts](../getting-started/concepts.md). + +--- + +**Admin** — highest user role. Full scope set. User management and task execution. See [Users & Roles](../administration/users-and-roles.md). + +**API token** — see Client API Token. + +**Argosy** — RomM's first-party Android launcher app. See [Argosy](../ecosystem/argosy.md). + +**Asset** — user-uploaded content attached to a ROM: save files, emulator states, screenshots. Stored under `/romm/assets/`. Per-user. [Saves & States](../using/saves-and-states.md). + +**Autologin** — OIDC feature that bypasses RomM's login page and redirects straight to the IdP. Set via `OIDC_AUTOLOGIN=true`. + +**Basic auth** — HTTP auth with username+password in a header. Supported for API calls. + +**BIOS** — see Firmware. + +**Client API Token** — long-lived bearer token scoped to a user. Used by companion apps. 25/user max. [Client API Tokens](../ecosystem/client-api-tokens.md). + +**Collection** — a named group of ROMs. Three flavours: standard (hand-curated), smart (rule-based), virtual (auto-generated by RomM). [Collections](../using/collections.md). + +**Console Mode** — separate `/console` UI optimised for TVs and gamepads. [Console Mode](../using/console-mode.md). + +**Device** — a registered endpoint that syncs with RomM. Tracked via [Device Sync Protocol](../ecosystem/device-sync-protocol.md). + +**Editor** — mid-tier user role. Edit content (ROMs, platforms, collections), upload; no user management. See [Users & Roles](../administration/users-and-roles.md). + +**EmulatorJS** — the in-browser retro emulator RomM uses for most platforms. [In-Browser Play](../using/in-browser-play.md). + +**Feed** — a URL endpoint that exposes a filtered library view in a third-party tool's expected format. [Feeds reference](feeds.md). + +**Firmware** — BIOS files some emulators need. Lives under `/romm/library/bios/` (or per-platform bios folders). [Firmware Management](../administration/firmware-management.md). + +**Full image** — the default RomM container variant, including EmulatorJS + Ruffle. `rommapp/romm:X.Y.Z`. See [Image Variants](../install/image-variants.md). + +**Game Data tab** — the ROM detail page tab for saves, states, and screenshots. User-specific. + +**gamelist.xml** — ES-DE / Batocera-compatible metadata format. RomM can both import (as a metadata source) and export. + +**Grout** — RomM's first-party Linux handheld companion (muOS, NextUI). [Grout](../ecosystem/grout.md). + +**Hasheous** — metadata provider doing hash-based matching (no API keys). [Metadata Providers → Hasheous](../administration/metadata-providers.md#hasheous). + +**IGDB** — Internet Game Database. Primary metadata provider. [Metadata Providers → IGDB](../administration/metadata-providers.md#igdb). + +**Igir** — third-party ROM collection manager. Useful for cleaning libraries before importing into RomM. [Igir](../ecosystem/igir.md). + +**Invite link** — single-use URL that lets a new user register with a pre-assigned role. [Invitations & Registration](../administration/invitations-and-registration.md). + +**Kekatsu** — Nintendo DS multiboot loader that reads RomM's feed. [Kekatsu](../ecosystem/kekatsu.md). + +**Kiosk mode** — `KIOSK_MODE=true` setting that turns every read endpoint into unauthenticated access. [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). + +**Library** — your ROM files on disk. Mounted as `/romm/library`. Platforms are subdirectories. [Folder Structure](../getting-started/folder-structure.md). + +**LaunchBox** — metadata provider; uses a local downloaded DB. + +**Metadata provider** — external source of game data (IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, PlayMatch, LaunchBox, SteamGridDB, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro — 13 total in 5.0). [Metadata Providers](../administration/metadata-providers.md). + +**mike** — versioning tool for MkDocs used by the RomM docs site. + +**MobyGames** — paid metadata provider. [Metadata Providers → MobyGames](../administration/metadata-providers.md#mobygames). + +**muOS** — custom firmware for ARM handhelds. RomM has a muOS-specific app. [muOS App](../ecosystem/muos-app.md). + +**Netplay** — multi-player EmulatorJS mode over WebRTC. [Netplay](../using/netplay.md). + +**OIDC** — OpenID Connect. Standard SSO protocol RomM supports for external auth. [OIDC Setup](../administration/oidc/index.md). + +**OpenTelemetry (OTEL)** — opt-in traces/metrics/logs export. `OTEL_ENABLED=true`. [Observability](../administration/observability.md). + +**Personal tab** — the ROM detail page tab for per-user data (rating, status, notes, playtime). + +**pkgj** — PS Vita / PSP homebrew installer. Consumes RomM feeds. [pkgj](../ecosystem/pkgj.md). + +**Platform** — a gaming system (SNES, PSX, GBA, etc.). Each has a canonical slug. [Supported Platforms](../platforms/supported-platforms.md). + +**Play session** — timestamped record of someone playing a ROM. + +**Playnite Plugin** — RomM's Windows Playnite integration. [Playnite Plugin](../ecosystem/playnite-plugin.md). + +**PWA** — Progressive Web App. Install RomM to your home screen. [Install as PWA](../using/pwa.md). + +**ROM** — a single game entry in RomM. Can be a single file or a multi-file folder. Belongs to exactly one platform. + +**RetroAchievements (RA)** — achievements service RomM integrates with. Per-user linking. [RetroAchievements](../using/retroachievements.md). + +**Resource** — provider-fetched metadata image (cover, screenshot, manual). Stored under `/romm/resources/`. Rebuildable from a rescan. + +**RomM** — this project. Pronounced "rom-em" (rhymes with "problem"). + +**RQ** — Redis Queue, the task-queue library RomM uses for background work. + +**Ruffle** — the in-browser Flash / Shockwave emulator RomM uses. [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle). + +**Scan** — walk the library, hash files, match metadata, update the DB. Six modes. [Scanning & Watcher](../administration/scanning-and-watcher.md). + +**Scope** — fine-grained permission. 19 in total, grouped into roles. [scope matrix](../administration/users-and-roles.md#scope-matrix). + +**ScreenScraper** — metadata provider with good artwork. [Metadata Providers → ScreenScraper](../administration/metadata-providers.md#screenscraper). + +**Setup Wizard** — first-run flow that creates the admin user. Shown before any user exists. + +**Slim image** — smaller container variant without EmulatorJS or Ruffle. `rommapp/romm:X.Y.Z-slim`. See [Image Variants](../install/image-variants.md). + +**Smart Collection** — rule-based auto-populating collection. [Smart Collections](../using/smart-collections.md). + +**Socket.IO** — RomM's WebSocket protocol. Two endpoints: `/ws` and `/netplay`. [WebSockets](../developers/websockets.md). + +**SteamGridDB** — alternate cover art provider. [Metadata Providers → SteamGridDB](../administration/metadata-providers.md#steamgriddb). + +**Task** — a unit of background work in RomM. Runs via RQ. Scheduled / manual / watcher. [Scheduled Tasks](../administration/scheduled-tasks.md). + +**TheGamesDB (TGDB)** — free community metadata provider. New in 5.0. + +**Tinfoil** — Nintendo Switch homebrew that installs from RomM's feed. [Tinfoil](../ecosystem/tinfoil.md). + +**User** — an account. One of three roles (Viewer / Editor / Admin). + +**Valkey** — open-source Redis fork; drop-in compatible. See [Redis or Valkey](../install/redis-or-valkey.md). + +**Viewer** — lowest user role. Read-only on library, own saves/states/profile. + +**Virtual Collection** — auto-generated collection by genre / developer / year / tag. Read-only. [Virtual Collections](../using/virtual-collections.md). + +**Watcher** — filesystem watcher that triggers scans on file events. `WATCHER_ENABLED=true`. [Scanning & Watcher](../administration/scanning-and-watcher.md#filesystem-watcher). + +**WebRcade** — alternative browser frontend that consumes RomM's feed. [WebRcade](../ecosystem/webrcade.md). + +--- + +Missing a term? Open a PR against this page. diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index c727cd18..c673e0ec 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -1,10 +1,133 @@ --- -status: placeholder -wave: 3 +title: Ports & Endpoints +description: What listens where inside a RomM container, and how external traffic reaches it. --- # Ports & Endpoints -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 3) and - has not been written yet. See the overhaul plan for status and ownership. +Quick reference for the ports + URL paths a RomM instance exposes. + +## Container ports + +| Port | Protocol | Purpose | Exposed? | +| --- | --- | --- | --- | +| `8080` | HTTP | nginx — the front door. Serves everything. | Yes — publish this. | +| `5000` | HTTP | gunicorn — FastAPI backend. Internal only. | No — nginx proxies here. | +| `6379` | TCP | Redis. Internal only (unless you externalise). | No. | + +Only **`8080`** should be reachable from outside the container in production. Typical compose `ports:`: + +```yaml +services: + romm: + ports: + - 80:8080 # bind host :80 to container :8080 + # or behind a reverse proxy: + # - 127.0.0.1:8080:8080 +``` + +## URL paths on `:8080` + +Everything below is served by nginx on port 8080. Auth / protection depends on the path. + +### Unauthenticated + +| Path | Purpose | +| --- | --- | +| `/login`, `/register`, `/reset-password` | Auth UI pages. | +| `/api/auth/login`, `/api/auth/logout` | Session endpoints. | +| `/api/auth/openid`, `/api/auth/oauth/openid` | OIDC callback flow. | +| `/api/heartbeat` | Health check. | +| `/openapi.json` | OpenAPI spec. | +| `/api/docs`, `/api/redoc` | Rendered API browsers. | +| Static assets (`/assets/...`, EmulatorJS bundle, Ruffle, covers served from `/resources/...`) | Served by nginx. | + +### Session-authenticated (cookies) + +Most of the RomM web UI — every page under `/`. Authenticated via browser cookie. + +### Token-authenticated (bearer) + +Every API endpoint under `/api/...` that requires a specific scope. Session cookies also work here; token auth is for scripts / companion apps. + +### Download endpoints (optionally auth-off) + +| Path | Required scope normally | With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` | +| --- | --- | --- | +| `/api/roms/{id}/content/{filename}` | `roms.read` | None | +| `/api/firmware/{id}/content/{filename}` | `firmware.read` | None | + +See [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass) for the security context. + +### Feed endpoints + +| Path | Client | Auth | +| --- | --- | --- | +| `/api/feeds/tinfoil` | [Tinfoil](../ecosystem/tinfoil.md) | Respects `DISABLE_DOWNLOAD_ENDPOINT_AUTH`; can send basic auth. | +| `/api/feeds/pkgi/...` | [pkgj](../ecosystem/pkgj.md) | Same. | +| `/api/feeds/fpkgi/...` | [fpkgi](../ecosystem/fpkgi.md) | Same. | +| `/api/feeds/kekatsu/...` | [Kekatsu](../ecosystem/kekatsu.md) | Same. | +| `/api/feeds/webrcade` | [WebRcade](../ecosystem/webrcade.md) | Same. | + +Full catalogue in [Feeds](feeds.md). + +### WebSocket endpoints + +| Path | Purpose | +| --- | --- | +| `/ws/socket.io` | General live updates (scans, tasks). | +| `/netplay/socket.io` | Netplay session coordination. | + +Both use Socket.IO; reverse proxy must pass through the upgrade. See [WebSockets](../developers/websockets.md) and [Reverse Proxy](../install/reverse-proxy.md). + +## Volumes (not ports but relevant) + +Inside the container: + +| Path | Purpose | Backup? | +| --- | --- | --- | +| `/romm/library` | Your ROM + firmware source. Typically read-only mount. | No (back up separately). | +| `/romm/assets` | User uploads (saves, states, screenshots). | **Critical.** | +| `/romm/resources` | Provider-fetched cover art, screenshots. | Low priority (rebuildable). | +| `/romm/config` | `config.yml`. | **Critical.** | +| `/redis-data` | Redis persistence for the in-container Redis. | Low priority. | + +See [Install & Deploy → Docker Compose](../install/docker-compose.md) for the full volume spec. + +## External services RomM talks to + +Outbound connections a running RomM instance may make: + +| Destination | Purpose | Optional? | +| --- | --- | --- | +| `api.igdb.com` | IGDB metadata. | Yes (if `IGDB_CLIENT_ID` set). | +| `www.screenscraper.fr` | ScreenScraper metadata. | Yes. | +| `www.mobygames.com` | MobyGames metadata. | Yes. | +| `retroachievements.org` | RA metadata + progression. | Yes. | +| `www.steamgriddb.com` | Cover art. | Yes. | +| `gamesdb.launchbox-app.com` | LaunchBox DB download. | Yes. | +| `hasheous.org` | Hash-based matching. | Yes. | +| Your OIDC provider | SSO. | Yes. | +| `sentry.io` or self-hosted Sentry | Error reporting. | Yes. | +| Your OTEL collector | Observability. | Yes. | + +If your firewall is egress-restrictive, allow-list these based on which features you've enabled. + +## Reverse proxy path rewriting + +Some users put RomM behind a path prefix (`/romm/...` instead of the bare host root). Set `ROMM_BASE_PATH` to match: + +```yaml +environment: + - ROMM_BASE_PATH=/romm +``` + +nginx inside the container rewrites paths internally; your reverse proxy forwards the full path. + +Not the most common pattern; most deployments use a subdomain (`romm.example.com`) rather than a path. + +## See also + +- [Reverse Proxy](../install/reverse-proxy.md) — passthrough recipes per proxy. +- [Environment Variables](environment-variables.md) — including `ROMM_PORT`, `ROMM_BASE_PATH`, `ROMM_BASE_URL`. +- [Architecture](../developers/architecture.md) — how the port layout fits into the bigger picture. From 7d669babe1074dd428cc826694f5fe6ed1e8b552 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 14:53:23 -0400 Subject: [PATCH 020/121] cleanup superfluous ci tasks --- .github/workflows/deploy-v5-preview.yml | 62 ----- .github/workflows/deploy.yml | 14 +- .github/workflows/pr-checks.yml | 2 +- .github/workflows/romm-release-bump.yml | 85 ------ docs/Getting-Started/Folder-Structure.md | 241 ------------------ .../folder-structure.md | 1 + 6 files changed, 3 insertions(+), 402 deletions(-) delete mode 100644 .github/workflows/deploy-v5-preview.yml delete mode 100644 .github/workflows/romm-release-bump.yml delete mode 100644 docs/Getting-Started/Folder-Structure.md rename docs/{getting-started => Getting-Started}/folder-structure.md (99%) diff --git a/.github/workflows/deploy-v5-preview.yml b/.github/workflows/deploy-v5-preview.yml deleted file mode 100644 index 0a3dbe05..00000000 --- a/.github/workflows/deploy-v5-preview.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Deploy v5 Preview - -# Builds the v5 branch and publishes via mike under the `next` alias. -# Never touches `latest` — that alias only moves on the manual deploy.yml run. - -on: - push: - branches: [v5] - -concurrency: - group: pages - cancel-in-progress: true - -permissions: - contents: write - pages: write - id-token: write - -jobs: - deploy: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Checkout repo - uses: actions/checkout@v4.3.0 - with: - fetch-depth: 0 - - - name: Install uv - uses: astral-sh/setup-uv@v6.7.0 - with: - enable-cache: true - cache-dependency-glob: uv.lock - - - name: Set up Python - uses: actions/setup-python@v6.0.0 - with: - python-version-file: .python-version - - - name: Install dependencies - run: uv sync --all-extras --dev - - - name: Refresh generated snippets - run: | - uv run python docs/scripts/gen_env_vars.py - uv run python docs/scripts/gen_platforms.py - uv run python docs/scripts/gen_scheduled_tasks.py - - - name: Set Git user - run: | - git config --global user.name ${{ secrets.GIT_NAME }} - git config --global user.email ${{ secrets.GIT_EMAIL }} - git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git - git fetch -a - git checkout gh-pages - git pull origin gh-pages - git checkout v5 - - - name: Deploy `next` alias via mike - run: uv run mike deploy --push --update-aliases 5.0-dev next - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cce0ca72..72091f6b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -28,18 +28,6 @@ jobs: with: fetch-depth: 0 - - name: Decide alias - id: alias - # Only `5.*` versions become `latest`. Older versions deploy as a - # frozen snapshot at // with no alias change. - run: | - version="${{ github.event.inputs.version }}" - if [[ "$version" =~ ^5(\.|$) ]]; then - echo "alias=latest" >> "$GITHUB_OUTPUT" - else - echo "alias=" >> "$GITHUB_OUTPUT" - fi - - name: Install uv uses: astral-sh/setup-uv@v6.7.0 with: @@ -72,6 +60,6 @@ jobs: git checkout main - name: Build the documentation - run: uv run mike deploy --push --update-aliases ${{ github.event.inputs.version }} ${{ steps.alias.outputs.alias }} + run: uv run mike deploy --push --update-aliases ${{ github.event.inputs.version }} latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 93fb9d89..81713660 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -2,7 +2,7 @@ name: PR Checks on: pull_request: - branches: [main, v5] + branches: [main] concurrency: group: pr-checks-${{ github.ref }} diff --git a/.github/workflows/romm-release-bump.yml b/.github/workflows/romm-release-bump.yml deleted file mode 100644 index 6afaca04..00000000 --- a/.github/workflows/romm-release-bump.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: RomM Release Bump - -# When rommapp/romm publishes a new release, update the pinned ref in -# docs/scripts/sources.toml, regenerate snippets, and open a PR. - -on: - schedule: - - cron: "0 6 * * *" # daily at 06:00 UTC - workflow_dispatch: - inputs: - ref: - description: "Override RomM ref (tag or SHA). Leave blank for latest release." - required: false - type: string - -permissions: - contents: write - pull-requests: write - -jobs: - bump: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Checkout repo - uses: actions/checkout@v4.3.0 - - - name: Resolve target ref - id: ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - override="${{ github.event.inputs.ref }}" - if [ -n "$override" ]; then - echo "ref=$override" >> "$GITHUB_OUTPUT" - else - latest=$(gh release view --repo rommapp/romm --json tagName -q .tagName) - echo "ref=$latest" >> "$GITHUB_OUTPUT" - fi - - - name: Update sources.toml - run: | - python - <<'PY' - import re, pathlib - ref = "${{ steps.ref.outputs.ref }}" - p = pathlib.Path("docs/scripts/sources.toml") - text = p.read_text() - new = re.sub(r'(?m)^ref\s*=.*$', f'ref = "{ref}"', text) - p.write_text(new) - PY - - - name: Install uv - uses: astral-sh/setup-uv@v6.7.0 - with: - enable-cache: true - cache-dependency-glob: uv.lock - - - name: Set up Python - uses: actions/setup-python@v6.0.0 - with: - python-version-file: .python-version - - - name: Install dependencies - run: uv sync --all-extras --dev - - - name: Regenerate snippets - run: | - uv run python docs/scripts/gen_env_vars.py - uv run python docs/scripts/gen_platforms.py - uv run python docs/scripts/gen_scheduled_tasks.py - - - name: Open PR - uses: peter-evans/create-pull-request@v7 - with: - commit-message: "Docs: sync with rommapp/romm@${{ steps.ref.outputs.ref }}" - branch: bot/romm-bump-${{ steps.ref.outputs.ref }} - title: "Docs: sync with rommapp/romm@${{ steps.ref.outputs.ref }}" - body: | - Auto-bump triggered by a new RomM release. - - - Pinned `docs/scripts/sources.toml` ref to `${{ steps.ref.outputs.ref }}`. - - Regenerated all source-of-truth snippets. - - Review the diff for any new env vars / endpoints / tasks that - need narrative documentation. diff --git a/docs/Getting-Started/Folder-Structure.md b/docs/Getting-Started/Folder-Structure.md deleted file mode 100644 index 6b75810f..00000000 --- a/docs/Getting-Started/Folder-Structure.md +++ /dev/null @@ -1,241 +0,0 @@ - - - -RomM requires one of these folder structures for proper operation. It will first attempt to detect **Structure A (recommended)**, and if not found, will fall back to **Structure B**. This auto-detection ensures flexibility while encouraging organization best practices. - -## Folder Organization - -RomM organizes content in two main categories: ROMs and BIOS files. - -- **Structure A (Recommended)**: Both ROMs and BIOS files have their own dedicated root folders, with platform folders inside each. - - - `/roms/{platform}/` - Contains all game files for that platform - - `/bios/{platform}/` - Contains all BIOS files for that platform - -- **Structure B (Fallback)**: Each platform has its own root folder containing both a ROMs folder and a BIOS folder. - - `/{platform}/roms/` - Contains all game files for that platform - - `/{platform}/bios/` - Contains all BIOS files for that platform - - -!!! note - The BIOS folder is entirely optional and only needed for platforms that require BIOS files. - -When using Docker, the volume mount point differs based on your chosen structure: - -- **Structure A**: Mount the parent folder of the `roms` folder -- **Structure B**: Mount the parent folder of the `platform` folders - -For multifile games (games stored as folders with multiple files or folders inside), RomM will detect special folders inside the game and will display with special tags in the webUI: - -- `dlc` -- `hack` -- `manual` -- `mod` -- `patch` -- `update` -- `demo` -- `translation` -- `prototype` - - -!!! tip - For folder naming conventions, review the [Platform Support](../Platforms-and-Players/Supported-Platforms.md) section. To override default system names in the folder structure (if your directories are named differently), see the [Configuration File](Configuration-File.md) section. - - - - - - - - - - - - - - -
Structure A (recommended)Structure B (fallback)
- library/roms/{platform}/{game} - - library/{platform}/roms/{game} -
-
-        library/
-        ├─ roms/
-        │  ├─ gbc/
-        │  │  ├─ game_1.gbc
-        │  │  └─ game_2.gbc
-        │  │
-        │  ├─ gba/
-        │  │  ├─ game_3.gba
-        │  │  └─ game_4/
-        │  │     ├─ game_4.gba
-        │  │     ├─ dlc
-        │  │     │  ├─ game_4_dlc_1.7z
-        │  │     │  └─ game_4_dlc_2.7z
-        │  │     ├─ hack
-        │  │     │  └─ game_4_hardmode.rar
-        │  │     ├─ manual
-        │  │     │  └─ game_4_manual.pdf
-        │  │     ├─ mod
-        │  │     │  └─ game_4_crazy_mode.zip
-        │  │     ├─ patch
-        │  │     │  └─ game_4_patch_v1.1.zip
-        │  │     ├─ update
-        │  │     ├─ demo
-        │  │     ├─ translation
-        │  │     └─ prototype
-        │  │
-        │  └─ ps/
-        │     ├─ game_5/
-        │     │   ├─ game_5_cd_1.iso
-        │     │   └─ game_5_cd_2.iso
-        │     │
-        │     └─ game_6.iso
-        │
-        └─ bios/
-           ├─ gba/
-           │  └─ gba_bios.bin
-           │
-           └─ ps/
-              ├─ scph1001.bin
-              ├─ scph5501.bin
-              └─ scph5502.bin
-      
-
-
-        library/
-        ├─ gbc/
-        │  └─ roms/
-        │     ├─ game_1.gbc
-        │     └─ game_2.gbc
-        │
-        ├─ gba/
-        │  ├─ roms/
-        │  │  ├─ game_3.gba
-        │  │  └─ game_4/
-        │  │     ├─ game_4.gba
-        │  │     ├─ dlc
-        │  │     │  ├─ game_4_dlc_1.7z
-        │  │     │  └─ game_4_dlc_2.7z
-        │  │     ├─ hack
-        │  │     │  └─ game_4_hardmode.rar
-        │  │     ├─ manual
-        │  │     │  └─ game_4_manual.pdf
-        │  │     ├─ mod
-        │  │     │  └─ game_4_crazy_mode.zip
-        │  │     ├─ patch
-        │  │     │  └─ game_4_patch_v1.1.zip
-        │  │     ├─ update
-        │  │     ├─ demo
-        │  │     ├─ translation
-        │  │     └─ prototype
-        │  │
-        │  └─ bios/
-        │     └─ gba_bios.bin
-        │
-        └─ ps/
-           ├─ roms/
-           │  ├─ game_5/
-           │  │  ├─ game_5_cd_1.iso
-           │  │  └─ game_5_cd_2.iso
-           │  │
-           │  └─ game_6.iso
-           │
-           └─ bios/
-              ├─ scph1001.bin
-              ├─ scph5501.bin
-              └─ scph5502.bin
-      
-
- - -!!! note - RomM can be setup without a prior folder structure. If files are manually uploaded from the webUI, RomM will automatically create the folder **Structure A** - -## Configuration file - -RomM's behavior can be customized using a `config.yml` file or through the `Library Management` page in the `Settings` menu. You can grab the example config.example.yml file and adapt it to your library. - -What is shown in the `Library Management` page is the content of the `config.yml`. For more details read the [configuration file](Configuration-File.md) section. - -## Naming Convention - -### Tag Support - -Games can be tagged with region, revision, or other tags by using parentheses in the file name. RomM will parse both tags with `[]` and `()`. - -- Regions and languages will be identified with both supported shortcodes and names. Additionally, you can set a custom region/language adding a `reg` or `reg-` prefix (e.g. `reg MyOwnLang` or `reg-MyOwnLang`) -- Revision tags must be prefixed with `rev` or `rev-` (e.g. `rev v1` or `rev-1`) -- Other tags will also be imported, for example: `tetris [1.0001](HACK)[!].gba` - -Tags can be used to search for games in the search bar. For example, searching for `(USA)` will return all games with the USA tag. - -
- -
- -#### Supported Languages - -| Code | Language | -| ------ | ----------- | -| Ar | Arabic | -| Da | Danish | -| De | German | -| El | Greek | -| En | English | -| Es | Spanish | -| Fi | Finnish | -| Fr | French | -| It | Italian | -| Ja | Japanese | -| Ko | Korean | -| Nl | Dutch | -| No | Norwegian | -| Pl | Polish | -| Pt | Portuguese | -| Ru | Russian | -| Sr | Serbian | -| Sv | Swedish | -| Zh | Chinese | -| nolang | No Language | - -
- -
- -#### Supported Regions - -| Code | Region | -| ---- | ------------- | -| A | Australia | -| AS | Asia | -| B | Brazil | -| C | Canada | -| CH | China | -| E | Europe | -| F | France | -| FN | Finland | -| G | Germany | -| GR | Greece | -| H | Holland | -| HK | Hong Kong | -| I | Italy | -| J | Japan | -| K | Korea | -| NL | Netherlands | -| NO | Norway | -| PD | Public Domain | -| R | Russia | -| S | Spain | -| SW | Sweden | -| T | Taiwan | -| U | USA | -| UK | England | -| UNK | Unknown | -| UNL | Unlicensed | -| W | World | - -
- -
diff --git a/docs/getting-started/folder-structure.md b/docs/Getting-Started/folder-structure.md similarity index 99% rename from docs/getting-started/folder-structure.md rename to docs/Getting-Started/folder-structure.md index ea5a9ea2..d3e24548 100644 --- a/docs/getting-started/folder-structure.md +++ b/docs/Getting-Started/folder-structure.md @@ -248,3 +248,4 @@ Tags are searchable in the search bar — typing `(USA)` returns every game tagg ## Filename metadata tags RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry — covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). + From 8b26c1893717538c99c832785d640ebfac7ad6e9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 19:02:11 +0000 Subject: [PATCH 021/121] docs: remove old Pascal-Case directories, normalize to lowercase IA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All content has been ported into the new lowercase-hyphenated IA. The old Pascal-Case folders were sitting alongside the new ones — visibly inconsistent and wasting repo space. Removed 11 top-level folders (35 files): - API-and-Development/ - Getting-Started/ - Integrations/ - Maintenance/ - Miscellaneous/ - OIDC-Guides/ - Platforms-and-Players/ - System-Setup/ - Tools/ - Troubleshooting/ - Usage/ The mkdocs-redirects plugin synthesizes redirect stubs from the redirect_maps in mkdocs.yml without needing the source files to exist — verified by building --strict and inspecting the generated Getting-Started/Quick-Start-Guide/index.html, which serves a correct -based redirect to getting-started/quick-start/. Every old URL continues to work; external links to docs.romm.app are preserved. --- docs/API-and-Development/API-Reference.md | 165 ----- docs/API-and-Development/Contributing.md | 81 --- docs/API-and-Development/Development-Setup.md | 181 ----- docs/API-and-Development/index.md | 14 - docs/Getting-Started/Authentication.md | 65 -- docs/Getting-Started/Configuration-File.md | 339 --------- docs/Getting-Started/Environment-Variables.md | 126 ---- docs/Getting-Started/Metadata-Providers.md | 191 ----- docs/Getting-Started/OIDC-Setup.md | 75 -- docs/Getting-Started/Quick-Start-Guide.md | 108 --- docs/Getting-Started/Reverse-Proxy.md | 162 ----- docs/Integrations/Playnite-plugin.md | 50 -- docs/Integrations/Tinfoil-integration.md | 58 -- docs/Integrations/muOS-app.md | 34 - docs/Integrations/pkgj-integration.md | 38 - docs/Maintenance/Migrating-to-new-machine.md | 93 --- docs/Maintenance/Scheduled-Tasks.md | 43 -- docs/Maintenance/Upgrading-to-3.0.md | 61 -- docs/Miscellaneous/Brand-Guidelines.md | 64 -- docs/Miscellaneous/FAQs.md | 17 - docs/OIDC-Guides/OIDC-Setup-With-Authelia.md | 96 --- docs/OIDC-Guides/OIDC-Setup-With-Authentik.md | 98 --- docs/OIDC-Guides/OIDC-Setup-With-Keycloak.md | 67 -- docs/OIDC-Guides/OIDC-Setup-With-PocketID.md | 52 -- docs/OIDC-Guides/OIDC-Setup-With-Zitadel.md | 82 --- .../Platforms-and-Players/Custom-Platforms.md | 12 - .../EmulatorJS-Player.md | 118 ---- docs/Platforms-and-Players/MS-DOS.md | 602 ---------------- docs/Platforms-and-Players/RuffleRS-Player.md | 7 - .../Supported-Platforms.md | 660 ------------------ docs/System-Setup/Synology-Setup-Guide.md | 119 ---- docs/System-Setup/TrueNAS-Setup-Guide.md | 74 -- docs/System-Setup/Unraid-Compose-Setup.md | 61 -- docs/System-Setup/Unraid-Setup-Guide.md | 67 -- docs/Tools/Igir-Collection-Manager.md | 124 ---- docs/Troubleshooting/Authentication-Issues.md | 18 - docs/Troubleshooting/Kubernetes-Issues.md | 26 - .../Miscellaneous-Troubleshooting.md | 20 - docs/Troubleshooting/Scanning-Issues.md | 44 -- docs/Troubleshooting/Synology-Issues.md | 31 - docs/Usage/Administration.md | 42 -- docs/Usage/LibraryManagement.md | 150 ---- docs/Usage/UserManagement.md | 24 - .../folder-structure.md | 1 - 44 files changed, 4560 deletions(-) delete mode 100644 docs/API-and-Development/API-Reference.md delete mode 100644 docs/API-and-Development/Contributing.md delete mode 100644 docs/API-and-Development/Development-Setup.md delete mode 100644 docs/API-and-Development/index.md delete mode 100644 docs/Getting-Started/Authentication.md delete mode 100644 docs/Getting-Started/Configuration-File.md delete mode 100644 docs/Getting-Started/Environment-Variables.md delete mode 100644 docs/Getting-Started/Metadata-Providers.md delete mode 100644 docs/Getting-Started/OIDC-Setup.md delete mode 100644 docs/Getting-Started/Quick-Start-Guide.md delete mode 100644 docs/Getting-Started/Reverse-Proxy.md delete mode 100644 docs/Integrations/Playnite-plugin.md delete mode 100644 docs/Integrations/Tinfoil-integration.md delete mode 100644 docs/Integrations/muOS-app.md delete mode 100644 docs/Integrations/pkgj-integration.md delete mode 100644 docs/Maintenance/Migrating-to-new-machine.md delete mode 100644 docs/Maintenance/Scheduled-Tasks.md delete mode 100644 docs/Maintenance/Upgrading-to-3.0.md delete mode 100644 docs/Miscellaneous/Brand-Guidelines.md delete mode 100644 docs/Miscellaneous/FAQs.md delete mode 100644 docs/OIDC-Guides/OIDC-Setup-With-Authelia.md delete mode 100644 docs/OIDC-Guides/OIDC-Setup-With-Authentik.md delete mode 100644 docs/OIDC-Guides/OIDC-Setup-With-Keycloak.md delete mode 100644 docs/OIDC-Guides/OIDC-Setup-With-PocketID.md delete mode 100644 docs/OIDC-Guides/OIDC-Setup-With-Zitadel.md delete mode 100644 docs/Platforms-and-Players/Custom-Platforms.md delete mode 100644 docs/Platforms-and-Players/EmulatorJS-Player.md delete mode 100644 docs/Platforms-and-Players/MS-DOS.md delete mode 100644 docs/Platforms-and-Players/RuffleRS-Player.md delete mode 100644 docs/Platforms-and-Players/Supported-Platforms.md delete mode 100644 docs/System-Setup/Synology-Setup-Guide.md delete mode 100644 docs/System-Setup/TrueNAS-Setup-Guide.md delete mode 100644 docs/System-Setup/Unraid-Compose-Setup.md delete mode 100644 docs/System-Setup/Unraid-Setup-Guide.md delete mode 100644 docs/Tools/Igir-Collection-Manager.md delete mode 100644 docs/Troubleshooting/Authentication-Issues.md delete mode 100644 docs/Troubleshooting/Kubernetes-Issues.md delete mode 100644 docs/Troubleshooting/Miscellaneous-Troubleshooting.md delete mode 100644 docs/Troubleshooting/Scanning-Issues.md delete mode 100644 docs/Troubleshooting/Synology-Issues.md delete mode 100644 docs/Usage/Administration.md delete mode 100644 docs/Usage/LibraryManagement.md delete mode 100644 docs/Usage/UserManagement.md rename docs/{Getting-Started => getting-started}/folder-structure.md (99%) diff --git a/docs/API-and-Development/API-Reference.md b/docs/API-and-Development/API-Reference.md deleted file mode 100644 index 80d9db7a..00000000 --- a/docs/API-and-Development/API-Reference.md +++ /dev/null @@ -1,165 +0,0 @@ -# API Reference - -RomM provides a comprehensive REST API that allows you to programmatically interact with your RomM instance. Most API endpoints are authenticated and follow RESTful conventions. - -## Interactive Documentation - -RomM automatically generates interactive API documentation using OpenAPI (Swagger). You can access the interactive API docs directly from your running instance: - -- **Swagger UI**: Available at `http://your-instance:3000/api/docs` -- **ReDoc**: Available at `http://your-instance:3000/api/redoc` - -These interactive docs allow you to: - -- Browse all available endpoints -- View request/response schemas -- Test API calls directly from your browser -- Understand authentication requirements -- Download the OpenAPI specification - -## Base URL - -The API base URL is typically: - -```text -http://your-instance:3000/api -``` - -Replace `your-instance` with your actual RomM instance URL or IP address. - -## Authentication - -All API endpoints require authentication. RomM supports: - -- **Basic HTTP Authentication** - Username and password -- **OAuth2 Password Bearer** - Token-based authentication (recommended for API usage) - -When using OAuth2, you'll need to obtain a token from `/api/token` endpoint and include it in the `Authorization` header as `Bearer `. - -### OAuth2 Scopes - -The API uses OAuth2 scopes to control access to different resources: - -**Read Scopes:** - -- `me.read` - View your profile -- `roms.read` - View ROMs -- `platforms.read` - View platforms -- `assets.read` - View assets -- `firmware.read` - View firmware -- `roms.user.read` - View user-rom properties -- `collections.read` - View collections -- `users.read` - View users - -**Write Scopes:** - -- `me.write` - Modify your profile -- `assets.write` - Modify assets -- `roms.user.write` - Modify user-rom properties -- `collections.write` - Modify collections -- `roms.write` - Modify ROMs -- `platforms.write` - Modify platforms -- `firmware.write` - Modify firmware -- `users.write` - Modify users -- `tasks.run` - Run tasks - -## API Endpoints Overview - -The RomM API provides comprehensive endpoints for managing all aspects of your ROM collection: - -### Core Resources - -- **Platforms** - Manage and configure gaming platforms -- **ROMs** - Full CRUD operations for ROM files with extensive filtering, searching, and metadata matching -- **Collections** - Create and manage ROM collections, smart collections, and virtual collections -- **Users** - User management, authentication, invite links, and profiles - -### Supporting Features - -- **Authentication** - OAuth2 token management, OIDC login, password resets -- **Search** - Metadata provider search for ROMs and covers -- **Tasks** - Background task management and execution -- **Assets** - Save files, states, screenshots management -- **Firmware** - Upload and manage firmware files for emulation -- **Configuration** - System configuration, platform bindings, and exclusions -- **Feeds** - Integration feeds for WebRcade and Tinfoil -- **Statistics** - System statistics and resource tracking - -For complete endpoint documentation including request/response schemas, query parameters, and authentication requirements, visit the interactive API documentation at `/api/docs` or `/api/redoc` on your RomM instance. - -## Example Usage - -### Using cURL - -```bash -# Get all libraries -curl -u username:password http://your-instance:3000/api/libraries - -# Get a specific ROM -curl -u username:password http://your-instance:3000/api/roms/123 - -# Create a new ROM entry -curl -X POST -u username:password \ - -H "Content-Type: application/json" \ - -d '{"name": "New ROM", "platform_id": 1}' \ - http://your-instance:3000/api/roms -``` - -### Using Python - -```python -import requests -from requests.auth import HTTPBasicAuth - -# Setup authentication -auth = HTTPBasicAuth('username', 'password') -base_url = 'http://your-instance:3000/api' - -# Get all libraries -response = requests.get(f'{base_url}/libraries', auth=auth) -libraries = response.json() - -# Get a specific ROM -response = requests.get(f'{base_url}/roms/123', auth=auth) -rom = response.json() -``` - -### Using JavaScript/Node.js - -```javascript -const axios = require("axios"); - -// Setup authentication -const api = axios.create({ - baseURL: "http://your-instance:3000/api", - auth: { - username: "username", - password: "password", - }, -}); - -// Get all libraries -const libraries = await api.get("/libraries"); - -// Get a specific ROM -const rom = await api.get("/roms/123"); -``` - -## OpenAPI Specification - -You can download the complete OpenAPI specification from your RomM instance: - -```text -http://your-instance:3000/openapi.json -``` - -This specification can be imported into API testing tools like Postman, used to generate client libraries, or used for API mocking. - -## Getting Help - -For API-specific questions or issues: - -1. Check the interactive documentation at `/api/docs` or `/api/redoc` on your instance -2. Review the code in the [RomM repository](https://github.com/rommapp/romm) -3. Open an issue on [GitHub](https://github.com/rommapp/romm/issues) -4. Join the [Discord community](https://discord.com/invite/romm) diff --git a/docs/API-and-Development/Contributing.md b/docs/API-and-Development/Contributing.md deleted file mode 100644 index c60a4080..00000000 --- a/docs/API-and-Development/Contributing.md +++ /dev/null @@ -1,81 +0,0 @@ -# Contributing to RomM - -Thank you for considering contributing to RomM! This document outlines some guidelines to help you get started with your contributions. - -**If you're looking to implement a large feature or make significant changes to the project, it's best to open an issue first AND join the Discord to discuss your ideas with the maintainers.** - -## Code of Conduct - -Please note that this project adheres to the Contributor Covenant [code of conduct](https://github.com/rommapp/romm/blob/master/CODE_OF_CONDUCT.md). By participating in this project, you are expected to uphold this code. - -## AI Assistance Notice - -> [!IMPORTANT] -> -> If you are using **any kind of AI assistance** to contribute to RomM, it must be disclosed in the pull request. - -If you are using any kind of AI assistance while contributing to RomM **this must be disclosed in the pull request**, along with the extent to which AI assistance was used (e.g. docs only vs. code generation). If PR responses are being generated by an AI, disclose that as well. As a small exception, trivial tab-completion doesn't need to be disclosed. - -An example disclosure: - -> This PR was written primarily by Claude Code. - -Or a more detailed disclosure: - -> I consulted ChatGPT to understand the codebase but the solution -> was fully authored manually by myself. - -Failure to disclose this is rude to the human operators on the other end of the pull request, but it also makes it difficult to determine how much scrutiny to apply to the contribution. - -In a perfect world, AI assistance would produce equal or higher quality work than any human. That isn't the world we live in today, and in most cases it's generating slop. - -Please be respectful to maintainers and disclose AI assistance. - -## Contributing to the Docs - -If you would like to contribute to the project's [documentation](https://docs.romm.app), open a pull request against [the docs repo](https://github.com/rommapp/docs). We welcome any contributions that help improve the documentation (new pages, updates, or corrections). - -## Adding Translations - -If you would like to translate the project into another language, create a new folder under the `frontend/src/locales` directory, and follow the existing language files as a template. Once you've created the new language file, open a pull request to add it to the project. - -## How to Contribute Code - -1. Fork the repository. -2. Clone your forked repository: `git clone https://github.com/your-username/romm.git` -3. Checkout the `master` branch: `git checkout master` -4. Follow the steps in the [developer setup guide](https://github.com/rommapp/romm/blob/master/DEVELOPER_SETUP.md) -5. Create a new branch for your feature/fix: `git checkout -b feature-or-fix-name` -6. Make your changes and commit them with descriptive commit messages: `git commit -am 'Add feature XYZ'` -7. Push your changes to your fork: `git push origin feature-or-fix-name` -8. Open a pull request to the `master` branch of the original repository. - -## Pull Request Guidelines - -- Make sure your code follows the project's coding standards. -- Test your changes locally before opening a pull request. -- Update the documentation if necessary. -- Ensure all existing tests pass, and add new tests for new functionality. -- Use clear and descriptive titles and descriptions for your pull requests. - -## Code Style - -Follow the existing code style used throughout the project. If working with VSCode or a similar editor, consider installing these extensions: - -- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) -- [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) -- [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) -- [Ruff](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) -- [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - -## Issue Reporting - -If you encounter any bugs or have suggestions for improvements, please [create an issue](https://github.com/rommapp/romm/issues) on GitHub. Provide as much detail as possible, including steps to reproduce the issue if applicable. - -## Licensing - -By contributing to RomM, you agree that your contributions will be licensed under the project's [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE). - ---- - -Thank you for contributing to RomM! Your help is greatly appreciated. diff --git a/docs/API-and-Development/Development-Setup.md b/docs/API-and-Development/Development-Setup.md deleted file mode 100644 index 0523a86d..00000000 --- a/docs/API-and-Development/Development-Setup.md +++ /dev/null @@ -1,181 +0,0 @@ - - -# Setting up RomM for development - -## Option 1: Using Docker - -If you prefer to use Docker for development, you can set up RomM using the provided Docker Compose configuration. This method simplifies the setup process by encapsulating all dependencies within Docker containers. - -### Environment setup - -#### Create the mock structure with at least one rom and empty config for manual testing - -```sh -mkdir -p romm_mock/library/roms/switch -touch romm_mock/library/roms/switch/metroid.xci -mkdir -p romm_mock/resources -mkdir -p romm_mock/assets -mkdir -p romm_mock/config -touch romm_mock/config/config.yml -``` - -#### Copy env.template to .env and fill the variables - -```sh -cp env.template .env -``` - -```dotenv -ROMM_BASE_PATH=/app/romm -DEV_MODE=true -``` - -#### Build the image - -```sh -docker compose build # or `docker compose build --no-cache` to rebuild from scratch -``` - -#### Spin up the Docker containers - -```sh -docker compose up -d -``` - -And you're done! You can access the app at `http://localhost:3000`. Any changes made to the code will be automatically reflected in the app thanks to the volume mounts. - -## Option 2: Manual setup - -### Environment setup - -#### - Create the mock structure with at least one rom and empty config for manual testing - -```sh -mkdir -p romm_mock/library/roms/switch -touch romm_mock/library/roms/switch/metroid.xci -mkdir -p romm_mock/resources -mkdir -p romm_mock/assets -mkdir -p romm_mock/config -touch romm_mock/config/config.yml -``` - -#### - Copy env.template to .env and fill the variables - -```sh -cp env.template .env -``` - -#### - Install system dependencies - -```sh -# https://mariadb.com/docs/skysql-previous-release/connect/programming-languages/c/install/#Installation_via_Package_Repository_(Linux): -sudo apt install libmariadb3 libmariadb-dev libpq-dev - -# Build and configure RAHasher (optional) -# This is only required to calculate RA hashes -# Users on macOS can skip this step as RAHasher is not supported -git clone --recursive https://github.com/RetroAchievements/RALibretro.git -cd ./RALibretro -git checkout 1.8.0 -git submodule update --init --recursive -sed -i '22a #include ' ./src/Util.h -make HAVE_CHD=1 -f ./Makefile.RAHasher -cp ./bin64/RAHasher /usr/bin/RAHasher -``` - -#### - Install python dependencies - -You'll need uv installed - - - -```sh -curl -LsSf https://astral.sh/uv/install.sh | sh -``` - -Then create the virtual environment and install the dependencies using uv: - -```sh -uv venv -source .venv/bin/activate -uv sync --all-extras --dev -``` - -#### - Spin up the database and other services - -```sh -docker compose up -d -``` - -#### - Run the backend - -_Migrations will be run automatically when running the backend._ - -```sh -cd backend -uv run python3 main.py -``` - -### Setting up the frontend - -#### - Install node.js dependencies - -```sh -cd frontend -# npm version >= 9 needed -npm install -``` - -#### - Create symlink to library and resources - -```sh -mkdir assets/romm -ln -s ../romm_mock/resources assets/romm/resources -ln -s ../romm_mock/assets assets/romm/assets -``` - -#### - Run the frontend - -```sh -npm run dev -``` - -## Setting up the linter - -We use [Trunk](https://trunk.io) for linting, which combines multiple linters and formatters with sensible defaults and a single configuration file. You'll need to install the Trunk CLI to use it. - -### - Install the Trunk CLI - -```sh -curl https://get.trunk.io -fsSL | bash -``` - -Alternative installation methods can be found [in their docs](https://docs.trunk.io/check/usage#install-the-cli). On commit, the linter will run automatically. To run it manually, use the following commands: - -```sh -trunk fmt -trunk check -``` - -**Failing to install and run the linter will result in a failed CI check, which won't allow us to merge your PR.** - -## Test setup - -### - Create the test user and database with root user - -```sh -docker exec -i romm-db-dev mariadb -uroot -p < backend/romm_test/setup.sql -``` - -### - Run tests - -_Migrations will be run automatically when running the tests._ - -```sh -cd backend -# path or test file can be passed as argument to test only a subset -uv run pytest [path/file] -# or run the following command to run all tests -# the -vv switch increases the verbosity of the output, providing more detailed information during test execution. -uv run pytest -vv -``` diff --git a/docs/API-and-Development/index.md b/docs/API-and-Development/index.md deleted file mode 100644 index 969ae7e9..00000000 --- a/docs/API-and-Development/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -search: - exclude: true ---- - -# API & Development - -Welcome to the RomM API & Development documentation. This section contains resources for developers looking to interact with RomM programmatically or contribute to its development. - -## Contents - -- **[API Reference](API-Reference.md)** - Complete API documentation with endpoints, schemas, and examples -- **[Contributing](Contributing.md)** - Guidelines for contributing code, translations, and documentation -- **[Development Setup](Development-Setup.md)** - How to set up your development environment diff --git a/docs/Getting-Started/Authentication.md b/docs/Getting-Started/Authentication.md deleted file mode 100644 index 0d58a7cf..00000000 --- a/docs/Getting-Started/Authentication.md +++ /dev/null @@ -1,65 +0,0 @@ - - -RomM provides support for various forms of authentication, granting flexibility in securing access to its features. - -### Setup - -You'll want to set the following environment variable before starting RomM: - -- `ROMM_AUTH_SECRET_KEY` is required and can be generated with `openssl rand -hex 32` - -### Sessions - -When the `/login` endpoint is called with valid credentials, a `session_id` is generated, stored as a cookie and sent to the browser. The same token is used to create a cache entry in Valkey (or in-memory if Valkey is disabled) which maps the token to the user. This way no sensitive information is stored on the client. - -### Roles - -A user can have one of the following roles: - -- VIEWER: Can view platforms and ROMs, download ROMs, and edit own profile -- EDITOR: Can create/edit/delete platforms and ROMs -- ADMIN: Can view all users, and create/edit/disable/delete users - -As permissions are additive, editors will have all permissions of the `viewer` role, and admins all those of the `editor` role. - -## Basic Authentication - -Requests can be made to protected API endpoints with an authorization header. The token is the base64 encoded value of `username:password`. - -Example using cURL: - -```bash -curl https://romm.local/api/platforms -H 'Authorization: Basic YWRtaW46aHVudGVyMg==' -``` - -## OAuth - -Along with the above forms of authentication, we've added an endpoint to generate expiring, scope-limited authentication tokens (`/api/token`). Successfully authenticating with that endpoint with return an `access_token` valid for 15 minutes, and a [`refresh_token`](https://oauth.net/2/grant-types/refresh-token/) valid for 2 weeks. The `refresh_token` can be used to generate a new `access_token` when needed. - -The `/api/token` endpoint requires a username, password, and a list of [scopes](https://oauth.net/2/scope/) in the format `read:roms write:roms read:platforms ...`. The list of scopes and endpoints are available to browse via Swagger UI or ReDoc (see next section). - -**Note: As of now, only the legacy [password grant type](https://oauth.net/2/grant-types/password/) is supported.** We plan to eventually add support for [Client Credentials](https://oauth.net/2/grant-types/client-credentials/). - -### OpenAPI - -The API endpoints are fully documented and compliant with the OpenAPI specification. Explore the API endpoints using the Swagger UI interface at `/api/docs` and the ReDoc interface at `/api/redoc`, or view the raw JSON at `/openapi.json`. - -For more information on OpenAPI, visit the [OpenAPI Specification](https://www.openapis.org/) website. - -## FAQ - -### Can I disable authentication? - -No, authentication is required and enabled by default. - -### I want to allow an EDITOR to edit ROMs but not delete them. Can I do that? - -At this time, fine-grain control over permissions within a role is not supported. This decision was taking in order to simplify user management in the client, and authentication/permission code on the server. - -### Is authentication safe/robust? Can I trust it? - -We've done our best to build an authentication system that is simple, clear and comprehensible. We have automated tests which verify that access is granted when it should be, and blocked when not (invalid credentials, missing permissions, expired access tokens, etc.). That being said, we welcome any reviews of our authentication and permission flows, PRs to fix issues, and new tests to cover edge cases. - -### I found an bug/issue with authentication. How do I report it? - -Please report bugs in our authentication/permission system privately by [submitting a vulnerability report](https://github.com/rommapp/romm/security/advisories/new). diff --git a/docs/Getting-Started/Configuration-File.md b/docs/Getting-Started/Configuration-File.md deleted file mode 100644 index 3b9ab30e..00000000 --- a/docs/Getting-Started/Configuration-File.md +++ /dev/null @@ -1,339 +0,0 @@ - - - -Below is a breakdown of each section of the `config.yml` file and its purpose. You can find a full example of the file in the [config.example.yml](https://github.com/rommapp/romm/blob/master/examples/config.example.yml) file. - ---- - -## Exclude Section - -Control which platforms, ROMs, or files to ignore during scanning. - -### Platforms - -Exclude entire platforms (folders) from being scanned. - -```yaml -exclude: - platforms: ["ps", "ngc", "gba"] -``` - -### ROMs - -Fine-tune which ROMs or files are excluded. - -#### Single File ROMs - -Applies to ROMs that are single files (not in subfolders). - -- **extensions**: Exclude files by extension - - Defaults to `["db", "ini", "tmp", "bak", "lock", "log", "cache", "crdownload"]` -- **names**: Exclude files by name or pattern (supports Unix wildcards) - - Defaults to `[".DS_Store", ".localized", ".Trashes", ".stfolder", "@SynoResource", "gamelist.xml"]` - -```yaml -exclude: - roms: - single_file: - extensions: ["xml", "txt"] - names: ["info.txt", "._*", "*.nfo"] -``` - -#### Multi-File ROMs - -Applies to ROMs stored as folders (multi-disc, with DLC, etc.). - -- **names**: Exclude entire folders by name - - Defaults to `["@eaDir", "__MACOSX", "$RECYCLE.BIN", ".Trash-*", ".stfolder", ".Spotlight-V100", ".fseventsd", ".DocumentRevisions-V100", "System Volume Information"]` -- **parts.names**: Exclude files by name or pattern from within multi-file ROM folders - - Defaults to `[".DS_Store", ".localized", ".Trashes", ".stfolder", "@SynoResource", "gamelist.xml"]` -- **parts.extensions**: Exclude files by extension from within multi-file ROM folders - - Defaults to `["db", "ini", "tmp", "bak", "lock", "log", "cache", "crdownload"]` - -```yaml -exclude: - roms: - multi_file: - names: ["final fantasy VII", "DLC"] - parts: - names: ["data.xml", "._*"] - extensions: ["xml", "txt"] -``` - ---- - -## System Section - -Customize how RomM interprets your folder and platform names. - -### Custom Folder Names - -Map your custom folder names to RomM's recognized platform names. - -```yaml -system: - platforms: - gc: "ngc" # Treats 'gc' folder as GameCube - psx: "ps" # Treats 'psx' folder as PlayStation -``` - -### Versions - -Associate a platform with its main version. This also tells RomM to fetch metadata from the main version source. - -```yaml -system: - versions: - naomi: "arcade" -``` - ---- - -## Filesystem Section - -Specify the folder name where your ROMs are located if it differs from the default. - -If your ROMs folder is named `my_roms` instead of `roms`: - -```yaml -filesystem: - roms_folder: "my_roms" -``` - -Disable file hash calculation for low power devices (e.g. Raspberry PI). - -```yaml -filesystem: - skip_hash_calculation: true -``` - ---- - -## Scan Section - -Configure metadata scanning priorities and media assets to download. - -### Priority - -Customize the order in which metadata providers are queried during scans. - -#### Metadata - -Controls metadata provider priority order. - -**Provider list in default order:** - -- `igdb` - IGDB (highest priority) -- `moby` - MobyGames -- `ss` - Screenscraper -- `ra` - RetroAchievements -- `launchbox` - Launchbox -- `gamelist` - ES-DE gamelist.xml -- `hasheous` - Hasheous -- `flashpoint` - Flashpoint Project -- `hltb` - HowLongToBeat (lowest priority) - -```yaml -scan: - priority: - metadata: - - "igdb" - - "ss" - - "moby" -``` - -#### Artwork - -Controls artwork provider priority order for cover art and screenshots. - -**Default:** Same as `priority.metadata` - -```yaml -scan: - priority: - artwork: - - "igdb" - - "ss" - - "moby" -``` - -#### Region - -Sets preferred region for cover art and game title (Screenscraper only). - -**Default:** `["us", "wor", "ss", "eu", "jp"]` - -```yaml -scan: - priority: - region: - - "us" - - "eu" - - "jp" -``` - -#### Language - -Sets preferred language for cover art and game title (Screenscraper only). - -**Default:** `["en", "fr"]` - -```yaml -scan: - priority: - language: - - "en" - - "es" - - "fr" -``` - -### Media - -Configures which media assets to download (Screenscraper and ES-DE gamelist.xml only). - -**Media types:** - -- `box2d` - Normal cover art (always enabled) -- `box3d` - 3D box art -- `miximage` - Mixed image of multiple media -- `physical` - Disc, cartridge, etc. -- `screenshot` - Screenshot (enabled by default) -- `title_screen` - Title screen -- `marquee` - Transparent logo -- `fanart` - User uploaded artwork -- `bezel` - Bezel displayed around the EmulatorJS window -- `manual` - Manual in PDF format (enabled by default) -- `video` - Gameplay video (warning: large file size) - -```yaml -scan: - media: - - box2d - - screenshot - - manual - - bezel -``` - -### Export gamelist.xml - -Automatically generate an ES-DE compatible gamelist.xml file placed in the platform folder for selected/discovered platforms. - -```yaml -scan: - export_gamelist: true -``` - ---- - -## EmulatorJS Section - -Configure EmulatorJS per-core options and controls. - -### Debug - -Enable debug mode to log available options to the browser console. - -```yaml -emulatorjs: - debug: true -``` - -### Cache Limit - -Cache limit per ROM in bytes. Set to `null` for unlimited. - -```yaml -emulatorjs: - cache_limit: 52428800 # 50 MB -``` - -### Disable batch bootup - -Skips the step that runs a batch file that sets soundcard information, mounts file systems, and attempts to run an `autorun.bat` file (try this if DOS games fail to boot). - -```yaml -emulatorjs: - disable_batch_bootup: true -``` - -### Disable auto-unload - -The emulator stops/shuts itself down when you navigate to a new page; this setting stops that behaviour (if that's something you want). - -```yaml -emulatorjs: - disable_auto_unload: true -``` - -### Netplay - -Enable [netplay](https://emulatorjs.org/docs4devs/netplay/#website-integration) and configure STUN/TURN servers. We recommend Google's public servers or [Metered's free tier](https://www.metered.ca/stun-turn). - -```yaml -emulatorjs: - netplay: - enabled: true - ice_servers: - - urls: "stun:stun.l.google.com:19302" - - urls: "stun:stun1.l.google.com:19302" - - urls: "stun:stun2.l.google.com:19302" - - urls: "turn:openrelay.metered.ca:80" - username: "openrelayproject" - credential: "openrelayproject" - - urls: "turn:openrelay.metered.ca:443" - username: "openrelayproject" - credential: "openrelayproject" -``` - - -!!! note - When netplay is enabled, EmulatorJS loads some assets (including localization files) from the nightly CDN (`https://cdn.emulatorjs.org/nightly/...`). This differs from stable mode, which uses local/bundled assets. Occasional temporary issues (e.g., 404 errors or untranslated UI elements) can occur if the nightly CDN has mismatches, but these usually resolve with the next EmulatorJS stable release integrated into RomM. - -### Settings - -Configure core-specific settings. Use `default` to apply settings to all cores. - -```yaml -emulatorjs: - settings: - parallel_n64: # Use the exact core name - vsync: disable - snes9x: - snes9x_region: ntsc - default: # These settings apply to all cores - fps: show -``` - -### Controls - -Map keyboard and controller controls for each player. - -**Example (2-player SNES):** - -```yaml -emulatorjs: - controls: - snes9x: - 0: # Player 1 - 0: # Button mapping - value: x # Keyboard mapping - value2: BUTTON_2 # Controller mapping - 1: # Player 2 - 0: - value: / - value2: BUTTON_2 -``` - -See [EmulatorJS documentation](https://emulatorjs.org/docs4devs/control-mapping/) for control mapping details. - ---- - - -!!! tip - You can find examples of full binded batocera or es-de config files. - - -!!! warning - Only uncomment or add the lines you need. Any omitted or empty sections will use RomM's defaults. - -For a full example, see the config.example.yml file. diff --git a/docs/Getting-Started/Environment-Variables.md b/docs/Getting-Started/Environment-Variables.md deleted file mode 100644 index 5e2a4e3b..00000000 --- a/docs/Getting-Started/Environment-Variables.md +++ /dev/null @@ -1,126 +0,0 @@ - - -This is a complete list of available environment variables; required variables are marked with a `✓`. - - -!!! tip - You can also set environment variables with a `_FILE` suffix, which will load the contents of the file specified in the variable into the variable without the suffix. For example, setting `ROMM_AUTH_SECRET_KEY_FILE=/run/secrets/romm_auth_secret_key` and creating a file with the secret key at the specified path will set `ROMM_AUTH_SECRET_KEY` to the contents of the file. [Learn more.](https://docs.docker.com/compose/how-tos/use-secrets/) - -## Application settings - -| Variable | Description | Required | Default | -| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | ------------------------- | -| ROMM_AUTH_SECRET_KEY | Generate a key with `openssl rand -hex 32` | ✓ | | -| DISABLE_CSRF_PROTECTION | Disables [CSRF protection](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) (not recommended) | | `false` | -| DISABLE_DOWNLOAD_ENDPOINT_AUTH | Disable auth on download endpoint (WebRcade, Tinfoil) | | `false` | -| DISABLE_USERPASS_LOGIN | Disables login with username and password (when using OIDC) | | `false` | -| SESSION_MAX_AGE_SECONDS | Maximum age of a session (in seconds) | | `1209600` (14 days) | -| KIOSK_MODE | Read-only mode for public displays or kiosks | | `false` | -| SCAN_TIMEOUT | Timeout for the background scan/rescan tasks (in seconds) | | `14400` (4 hours) | -| SCAN_WORKERS | Number of worker processes for scanning tasks | | `1` | -| TASK_TIMEOUT | Timeout for the the rest of the background tasks (in seconds) | | `300` (5 minutes) | -| TASK_RESULT_TTL | How long to keep task results in Valkey (in seconds) | | `86400` (24 hours) | -| SEVEN_ZIP_TIMEOUT | Timeout for 7-Zip operations (in seconds) | | `60` | -| DISABLE_EMULATOR_JS | Disables playing in browser with [EmulatorJS](../Platforms-and-Players/EmulatorJS-Player.md) | | `false` | -| DISABLE_RUFFLE_RS | Disables playing flash games with [RuffleRS](../Platforms-and-Players/RuffleRS-Player.md) | | `false` | -| YOUTUBE_BASE_URL | Base URL for alternate frontends (Piped, Invidious, etc.) | | `https://www.youtube.com` | -| TZ | Sets the timezone | | `UTC` | -| ROMM_PORT | Port on which the application listens | | `8080` | -| ROMM_BASE_PATH | Base folder path for library, resources and assets | | `/romm` | -| ROMM_BASE_URL | Base url for properly display container logs links (like register or recover password) | | `0.0.0.0` | -| LOGLEVEL | Logging level for the app | | `INFO` | -| FORCE_COLOR | Forces color output | | `false` | -| NO_COLOR | Disables color output | | `false` | -| WEB_SERVER_CONCURRENCY | Number of processes running the app | | `1` | -| WEB_SERVER_KEEPALIVE | Waiting time for requests on a Keep-Alive connection (in seconds) | | `2` | -| WEB_SERVER_MAX_REQUESTS | Maximum number of requests a worker will process before restarting | | `1000` | -| WEB_SERVER_MAX_REQUESTS_JITTER | Random jitter to add to the maximum number of requests a worker will process before restarting | | `100` | -| WEB_SERVER_TIMEOUT | Timeout for web server requests (in seconds) | | `300` | -| WEB_SERVER_WORKER_CONNECTIONS | Maximum number of simultaneous clients a single process can handle | | `1000` | - -## Dependencies - -| Variable | Description | Required | Default | -| -------------- | ---------------------------------------------------------------------------------------------------------- | :------: | ----------- | -| DB_HOST | Host name of database instance | ✓ | | -| DB_PORT | Port number of database instance | | `3306` | -| DB_NAME | Should match MYSQL_DATABASE in MariaDB | | `romm` | -| DB_USER | Database username (in MariaDB, should match MARIADB_USER) | ✓ | | -| DB_PASSWD | Database password (in MariaDB, should match MARIADB_PASSWORD) | ✓ | | -| DB_QUERY_JSON | Extra query parameters for the database connection, in JSON format (e.g. `{"unix_socket": "/path/to/db"}`) | | | -| ROMM_DB_DRIVER | Database driver to use (options: `mariadb`, `mysql`, `postgresql`) | | `mariadb` | -| REDIS_HOST | Host name of Redis/Valkey instance | | `127.0.0.1` | -| REDIS_PORT | Port number of Redis/Valkey instance | | `6379` | -| REDIS_USERNAME | Username for Redis/Valkey instance | | | -| REDIS_PASSWORD | Password for Redis/Valkey instance | | | -| REDIS_DB | Database number for Redis/Valkey instance | | `0` | -| REDIS_SSL | Enable SSL for Redis/Valkey instance | | `false` | -| SENTRY_DSN | DSN for Sentry error tracking | | | - -## Metadata providers - -| Variable | Description | Required | Default | -| ------------------------------------ | -------------------------------------------------- | :------: | ------- | -| IGDB_CLIENT_ID | Client ID for IGDB API | | | -| IGDB_CLIENT_SECRET | Client secret for IGDB API | | | -| SCREENSCRAPER_USER | Screenscraper username | | | -| SCREENSCRAPER_PASSWORD | Screenscraper password | | | -| MOBYGAMES_API_KEY | MobyGames secret API key | | | -| STEAMGRIDDB_API_KEY | SteamGridDB secret API key | | | -| RETROACHIEVEMENTS_API_KEY | Retroachievements secret API key | | | -| REFRESH_RETROACHIEVEMENTS_CACHE_DAYS | Retroachievements metadata cache refresh (in days) | | `30` | -| LAUNCHBOX_API_ENABLED | Enable LaunchBox API integration | | `false` | -| PLAYMATCH_API_ENABLED | Enable PlayMatch API integration | | `false` | -| HASHEOUS_API_ENABLED | Enable Hasheous API integration | | `false` | -| FLASHPOINT_API_ENABLED | Enable Flashpoint API integration | | `false` | -| HLTB_API_ENABLED | Enable HowLongToBeat API integration | | `false` | - -## Authentication - -| Variable | Description | Required | Default | -| ---------------------------------- | ------------------------------------------------- | :------: | -------------------- | -| OIDC_ENABLED | Enable OpenID Connect (OIDC) authentication | | `false` | -| OIDC_PROVIDER | Name of the OIDC provider in use | | | -| OIDC_CLIENT_ID | Client ID for OIDC authentication | | | -| OIDC_CLIENT_SECRET | Client secret for OIDC authentication | | | -| OIDC_REDIRECT_URI | Absolute redirect URI for OIDC authentication | | | -| OIDC_SERVER_APPLICATION_URL | Absolute URL of the OIDC server application | | | -| OIDC_TLS_CACERTFILE | Path to a file containing trusted CA certificates | | | -| OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS | Access token lifetime (in seconds) | | `1800` (30 minutes) | -| OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS | Refresh token lifetime (in seconds) | | `604800` (7 days) | -| INVITE_TOKEN_EXPIRY_SECONDS | Invite token lifetime (in seconds) | | `600` (10 minutes) | -| OIDC_SERVER_METADATA_URL | URL to the OIDC provider metadata endpoint | | | -| OIDC_RP_INITIATED_LOGOUT | Enable RP-initiated logout flow | | `false` | -| OIDC_END_SESSION_ENDPOINT | OIDC end-session endpoint override URL | | `""` | -| OIDC_USERNAME_ATTRIBUTE | Attribute on OIDC user info used as the username | | `preferred_username` | -| OIDC_AUTOLOGIN | Skip click OIDC button on login page | | `false` | -| OIDC_CLAIM_ROLES | OIDC claim containing user roles | | | -| OIDC_ROLE_VIEWER | Role value mapping to viewer permissions | | | -| OIDC_ROLE_EDITOR | Role value mapping to editor permissions | | | -| OIDC_ROLE_ADMIN | Role value mapping to admin permissions | | | - -## Background tasks - -| Variable | Description | Required | Default | -| ------------------------------------------------ | --------------------------------------------------------------------- | :------: | ------------- | -| ENABLE_RESCAN_ON_FILESYSTEM_CHANGE | Enable re-scanning of library when filesystem changes | | `false` | -| RESCAN_ON_FILESYSTEM_CHANGE_DELAY | Delay before re-scanning library when filesystem changes (in minutes) | | `5` | -| ENABLE_SCHEDULED_RESCAN | Enable scheduled re-scanning of library | | `false` | -| SCHEDULED_RESCAN_CRON | Cron expression for scheduled re-scanning | | `"0 3 * * *"` | -| ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB | Enable scheduled updating of Switch TitleDB index | | `false` | -| SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON | Cron expression for scheduled updating of Switch TitleDB | | `"0 4 * * *"` | -| ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA | Enable scheduled updating of LaunchBox metadata | | `false` | -| SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON | Cron expression for scheduled updating of LaunchBox metadata | | `"0 4 * * *"` | -| ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP | Enable scheduled conversion of images to WebP format | | `false` | -| SCHEDULED_CONVERT_IMAGES_TO_WEBP_CRON | Cron expression for scheduled conversion of images to WebP format | | `"0 4 * * *"` | -| ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC | Enable scheduled syncing of Retroachievements progress | | `false` | -| SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC_CRON | Cron expression for scheduled syncing of Retroachievements progress | | `"0 4 * * *"` | - -## Development - -| Variable | Description | Required | Default | -| ------------ | -------------------------------------------------- | :------: | ----------- | -| DEV_MODE | Enable development mode (debugging, hot-reloading) | | `false` | -| DEV_HOST | Host for development server | | `127.0.0.1` | -| DEV_PORT | Port for development server | | `5000` | -| DEV_SQL_ECHO | Enable SQL query logging in development mode | | `false` | diff --git a/docs/Getting-Started/Metadata-Providers.md b/docs/Getting-Started/Metadata-Providers.md deleted file mode 100644 index 1baaa73f..00000000 --- a/docs/Getting-Started/Metadata-Providers.md +++ /dev/null @@ -1,191 +0,0 @@ - - - -RomM supports multiple metadata providers to enrich your game library with titles, descriptions, cover art, and achievements. You don't need all providers, so this guide covers [popular combos](#popular-combos) and [setup instructions](#setup-instructions). - -## Popular combos - -Here are some combinations you can use based on your needs: - -#### ⭐ The French Connection: [ScreenScraper](#screenscraper) + [Retroachievements](#retroachievements) - -- Supports 125+ popular systems -- ScreenScraper provides titles, descriptions, cover art, screenshots and manuals - - Also supports hash-based matching (as of `v4.4`) - - With the option for 3D boxes and CD/cartridge covers -- Retroachievements provides achievement progress -- **Use this if you want to avoid Twitch/Amazon products** - -![ScreenScraper + Retroachievements](../resources/metadata_providers/3dboxes.png) - -#### ⭐ The Chef's Choice: [Hasheous](#hasheous) + [IGDB](#igdb) + [SteamGridDB](#steamgriddb) + [Retroachievements](#retroachievements) - -- Supports 135+. popular systems -- Hasheous provides hash-based matching and proxies IGDB data (titles, descriptions and artwork) -- IGDB adds additional metadata like related games and screenshots -- SteamGridDB provides high-quality alternative cover art -- Retroachievements provides achievement progress -- **This is the recommended setup for most users** - -![Hasheous + IGDB + SteamGridDB + Retroachievements](../resources/metadata_providers/2dcovers.png) - -#### The Twitch Fanboy: [IGDB](#igdb) + [PlayMatch](#playmatch) - -- Supports the 200+ systems available on IGDB -- Provides titles, descriptions, cover art and related games from IGDB -- PlayMatch adds hash-based matching for unmatched files -- **Use this if you want a single-provider solution** - -#### The Quick Starter: [Hasheous](#hasheous) - -- Hash-based matching only ⚠️ -- Proxies titles, descriptions and cover art from IGDB -- Incredibly fast scan times -- **For users who want to avoid API keys** - -## Setup instructions - -### IGDB - -[IGDB](https://www.igdb.com/) (Internet Game Database) is a popular metadata provider that offers metadata, cover art, screenshots, related games and more. - -To access the IGDB API you'll need a Twitch account and a valid phone number for 2FA verification. Up-to-date instructions are available in the [IGDB API documentation](https://api-docs.igdb.com/#account-creation). When registering your application in the Twitch Developer Portal, fill out the form like so: - -- Name: Something **unique or random** like `romm-3fca6fd7f94dea4a05d029f654c0c44b` or `KVV8NDXMSRFJ2MRNPNRSL7GQT` -- OAuth Redirect URLs: `localhost` -- Category: `Application Integration` -- Client Type: `Confidential` - - -!!! important - The name you pick has to be unique! Picking an existing name will fail silently, with no error messages. We recommend using `romm-`, like `romm-3fca6fd7f94dea4a05d029f654c0c44b` - -Note the client ID and secret that appear on screen, and use them to set `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` in your environment variables. - - -??? Screenshots - ![IGDB Creation](../resources/metadata_providers/1-igdb.png) - ![IGDB Secret](../resources/metadata_providers/2-igdb.png) - -### ScreenScraper - -[ScreenScraper.fr](https://screenscraper.fr/) is a French provider that offers metadata, cover art, screenshots and manuals, along with the option for 3D boxes and CD/cartridge cover art. It supports a wide range of systems and is a great alternative to IGDB. - -To access the ScreenScraper API, create a [ScreenScraper](https://www.screenscraper.fr/membreinscription.php) account and copy the **user** and **password** you just created to `SCREENSCRAPER_USER` and `SCREENSCRAPER_PASSWORD` respectively. - -### MobyGames - -MobyGames is a metadata provider that offers metadata, cover art and screenshots. - -To access the MobyGames API, [create a MobyGames account](https://www.mobygames.com/user/register/) and then visit your profile page. Click the **API** link under your user name to sign up for an API key. Copy the key shown and use it to set `MOBYGAMES_API_KEY`. - - -!!! important - Access to the MobyGames API is a [paid feature](https://www.mobygames.com/info/api/#non-commercial). While we will continue to support it, we recommend using [ScreenScraper](#screenscraper) instead, as it is free to use. - -### LaunchBox - -The [LaunchBox](https://gamesdb.launchbox-app.com/) Games Database is a community-driven database that provides metadata, cover art, and screenshots. Like the Launchbox desktop application, RomM downloads the entire database locally and matches games based on their exact filenames. - -To enable LaunchBox, set `LAUNCHBOX_API_ENABLED=true` and `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=true` in your environment variables. You can customize scheduled updates of the database by setting the frequency on the cron job with `SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON` (defaults to 5:00 AM every day). - -You must run a LaunchBox metadata update (either manually, or scheduled via cron) to generate a local `.xml` file with Launchbox metadata before using it as a metadata provider. The server will parse the local `.xml` file when trying to match a ROM and fetch metadata from this source. - -### Hasheous - -[Hasheous](https://hasheous.org/) is a free, open-source metadata provider that uses file hashes to match games. It proxies IGDB data for titles, descriptions, and cover art, and can provide Retroachievements IDs for matched games. - -Simply set `HASHEOUS_API_ENABLED=true` in your environment variables, and future scans will start using the [Hasheous API](https://hasheous.org/swagger/index.html). - -### PlayMatch - -[PlayMatch](https://github.com/RetroRealm/playmatch) is a hash-based matching service used in conjunction with IGDB to provide better matching for games, hosted by a member of our community. - -To enable PlayMatch, set `PLAYMATCH_API_ENABLED=true` in your environment variables. - -### SteamGridDB - -SteamGridDB provides custom cover art for games or collections. It's not accessed through the scanner but from the "search cover" button when manually editing a game. - -To access the SteamGridDB API, you need to login to their [website](https://www.steamgriddb.com/) with a [Steam account](https://store.steampowered.com/join). Once logged in, go to your [API tab under the preferences page](https://www.steamgriddb.com/profile/preferences/api). Copy the key shown and use it to set `STEAMGRIDDB_API_KEY`. - -### RetroAchievements - -RomM is able to display your achievements from [RetroAchievements](https://retroachievements.org/). To sync it with your RomM instance, you need to generate an API key from your RetroAchievements account in your [settings](https://retroachievements.org/settings). - -Copy the key shown and use it to set `RETROACHIEVEMENTS_API_KEY` and perform a `UNMATCHED` scan targeting the platform you want to match with RetroAchievements. - -After that, each user needs to set their own username in their profile and sync it with RetroAchievements. A new `Achievements` tab will appear in the `Personal` tab in the game details. - -To avoid unnecessary API calls, a cached file with the RA database is stored in RomM. Refresh time for that cache file can be changed with the environment variable `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS`. - - -??? Screenshots - ![RA API key](../resources/metadata_providers/1-ra.png) - ![RA details](../resources/metadata_providers/2-ra.png) - -### Flashpoint - -The [Flashpoint Project Database](https://flashpointproject.github.io/flashpoint-database/) is a project that enables metadata for 180,000+ flash and browser-based games. Enable this metadata source with the `FLASHPOINT_API_ENABLED=true` environment variable. If you are adding this provider to an existing RomM setup, perform a `UNMATCHED` scan with Flashpoint selected to update an existing platform. - -### How Long To Beat - -The [How Long To Beat](https://howlongtobeat.com/) project provides game completion times for more than 84,000 games. Enable this metadata source with the `HLTB_API_ENABLED=true` environment variable. If you are adding this provider to an existing RomM setup, perform a `UNMATCHED` scan with How Long To Beat selected to update an existing platform. - -Game completion times will be added to a new tab on the details page for supported matched games. - -### ES-DE gamelist.xml - -EmulationStation, and it's modern successor ES-DE, use a custom XML format to store game metadata. RomM can parse this format and import the assets as cover art and screenshots. You'll need to store the gamelist.xml file and any related assets under the platform folder: - - -```yaml -library/ -└─ roms/ - └─ gba/ - ├─ game_1.gba - ├─ game_2.gba - ├─ gamelist.xml - ├─ 3dboxes/ - │ ├─ game_1.png - │ └─ game_2.png - ├─ covers/ - ├─ screenshots/ - └─ etc... -``` - -#### ES-DE settings - -Here are the settings you need to change so RomM can read your artwork and gamelist.xml files from the same folder that holds your ROMs. - -1. Open the ES-DE settings file: - - - Linux / macOS: `~/ES-DE/settings/es_settings.xml` - - Windows: `C:\Program Files\ES-DE\settings\es_settings.xml` - -2. Make these two edits (add the lines if they don’t exist): - -```xml - - -``` - - - `MediaDirectory="/path/to/ROMs/folder"` download artwork into the same directory that contains the ROMs (should match `ROMDirectory`) - - `LegacyGamelistFileLocation="true"` forces gamelist.xml to be written next to the ROMs instead of inside the ES-DE config folder - -3. If you already have scraped artwork, copy/move the systems from `~/ES-DE/downloaded_media/` and `~/ES-DE/gamelists/` into your ROMs folder - -After a restart, ES-DE will place new artwork and the updated gamelist.xml directly in `roms//`, which is the layout RomM expects. - -## Metadata Tags in Filenames - -Scans will now parse custom metadata tags in the filename that match specific patterns, and use them to fetch game metadata for the specified ID. The supported tags are: - -(igdb-xxxx) for [IGDB](https://www.igdb.com/) -(moby-xxxx) for [MobyGames](https://www.mobygames.com/) -(ra-xxxx) for [RetroAchievements](https://retroachievements.org/) -(ssfr-xxxx) for [ScreenScraper](https://screenscraper.fr/) -(launchbox-xxxx) for [Launchbox](https://gamesdb.launchbox-app.com/) -(hltb-xxxx) for [HowLongToBeat](https://howlongtobeat.com/) - -Filenames will not be renamed by RomM to add tags, as they are a non-standard formatting system and could create conflicts with other software. diff --git a/docs/Getting-Started/OIDC-Setup.md b/docs/Getting-Started/OIDC-Setup.md deleted file mode 100644 index 399b4ae6..00000000 --- a/docs/Getting-Started/OIDC-Setup.md +++ /dev/null @@ -1,75 +0,0 @@ -# OIDC Setup - -OpenID Connect (OIDC) allows you to authenticate to RomM using external identity providers, enabling Single Sign-On (SSO) and centralized user management. This setup eliminates the need to manage separate credentials for RomM. - -## What is OAuth2? - -OAuth2 (Open Authorization 2.0) is an industry-standard protocol for authorization. It allows applications (clients) to gain limited access to user accounts on an HTTP service without sharing the user’s credentials. Instead, it uses access tokens to facilitate secure interactions. OAuth2 is commonly used in scenarios where users need to authenticate via a third-party service. - -## What is OpenID Connect (OIDC)? - -OIDC (OpenID Connect) is an identity layer built on top of OAuth2. While OAuth2 primarily handles authorization, OIDC adds authentication, enabling applications to verify a user’s identity and obtain profile information. This makes OIDC suitable for SSO solutions, where user identity is central to access management. - -## How It Works - -1. Click the OIDC login button on RomM's login page -2. You're redirected to your identity provider -3. Authenticate with your credentials -4. You're redirected back to RomM and logged in automatically - -## Supported Identity Providers - -RomM supports OIDC authentication with the following identity providers: - -### [Authelia](../OIDC-Guides/OIDC-Setup-With-Authelia.md) - -An open-source authentication and authorization server providing two-factor authentication and SSO. Ideal for self-hosters looking for a lightweight solution. - -### [Authentik](../OIDC-Guides/OIDC-Setup-With-Authentik.md) - -An open-source identity provider with support for modern authentication protocols, MFA, and comprehensive user management. - -### [PocketID](../OIDC-Guides/OIDC-Setup-With-PocketID.md) - -A simple OIDC provider that exclusively supports passkey authentication - no passwords required. - -### [Zitadel](../OIDC-Guides/OIDC-Setup-With-Zitadel.md) - -An enterprise-grade, open-source identity and access management platform supporting OAuth2, OIDC, SAML, and passwordless authentication. - -### [Keycloak](../OIDC-Guides/OIDC-Setup-With-Keycloak.md) - -A popular open-source OIDC provider with extensive features for identity and access management. - -## General Setup Requirements - -Regardless of which provider you choose, you'll need to configure these environment variables in RomM: - -```env -OIDC_ENABLED=true -OIDC_PROVIDER= -OIDC_CLIENT_ID= -OIDC_CLIENT_SECRET= -OIDC_REDIRECT_URI=/api/oauth/openid -OIDC_SERVER_APPLICATION_URL= -``` - -## Important Notes - -- **Email matching**: Your email address in RomM must match the email in your identity provider -- **First-time users**: Users logging in via OIDC for the first time will be created automatically with viewer permissions -- **Existing users**: Users who already have an account in RomM need to ensure their email addresses match between RomM and the identity provider - -## Troubleshooting - -If you encounter issues with OIDC authentication: - -- Verify all environment variables are set correctly -- Check that the redirect URI matches exactly between RomM and your identity provider -- Ensure your email address in RomM matches your email in the identity provider -- Review the [Authentication Issues](../Troubleshooting/Authentication-Issues.md) documentation -- Check your identity provider's logs for any authentication errors - -## Next Steps - -Choose a provider from the list above and follow its specific setup guide. Each guide provides detailed step-by-step instructions for configuring both the identity provider and RomM. diff --git a/docs/Getting-Started/Quick-Start-Guide.md b/docs/Getting-Started/Quick-Start-Guide.md deleted file mode 100644 index 5fa99ed1..00000000 --- a/docs/Getting-Started/Quick-Start-Guide.md +++ /dev/null @@ -1,108 +0,0 @@ - - - -This quick start guide will help you get a RomM instance up and running. It is split into 3 parts: Prepare, Build and Configure. - -## Prepare - -There are a few things you need to have in place before you can start: - -- [Docker](https://docs.docker.com/get-docker/) installed and running on your system -- Your ROMs organized in the correct [folder structure](./Folder-Structure.md) -- The recommended [metadata providers](./Metadata-Providers.md) set up -- A copy of the [config.yml](./Configuration-File.md) in the config folder for your environment - - -!!! warning - RomM works without a metadata API for basic use, but may cause issues with plugins like Playnite. Setting up IGDB API keys is recommended to prevent setup problems. - -## Build - -Now that we have everything gathered, we can begin getting your instance set up! - -1. Download a copy of the latest docker-compose.example.yml file from GitHub -2. Edit the file and modify the following values to configure the database - - `MARIADB_ROOT_PASSWORD`: Sets the root password of the database. Use a unique and secure password (_use a password generator for simplicity_) - - `MARIADB_DATABASE`: Sets the database name for RomM. This can be modified - but it's not necessary - - `MARIADB_USER`: User to connect to the database with. This can be modified - but it's not necessary - - `MARIADB_PASSWORD`: Password for the user to connect to the database with. Use a unique and secure password (_use a password generator for simplicity_) -3. Modify the following values in the **environment** to configure the application. _-- Other values can be changed, but should not be done unless you know what you are doing, and are outside the scope of this guide_ - - `DB_NAME`: Name of the database set in the database section - - `DB_USER`: Name of the user to connect to the database - - `DB_PASSWD`: Password of the user to connect to the database -4. Run the following command in a terminal and save the output to the `ROMM_AUTH_SECRET_KEY` environment variable: - ```sh - openssl rand -hex 32 - ``` - It should look something like this: - ```sh - 03a054b6ca27e0107c5eed552ea66becd9f3a2a8a91e7595cd462a593f9ecd09 - ``` -5. Add your metadata sources API keys: - - IGDB: `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` - - ScreenScraper.fr: `SCREENSCRAPER_USER` and `SCREENSCRAPER_PASSWORD` - - RetroAchievements: `RETROACHIEVEMENTS_API_KEY` - - MobyGames: `MOBYGAMES_API_KEY` - - SteamGridDB: `STEAMGRIDDB_API_KEY` - - Hasheous: `HASHEOUS_API_ENABLED=true` - - PlayMatch: `PLAYMATCH_API_ENABLED=true` - - LaunchBox: `LAUNCHBOX_API_ENABLED=true` -6. Modify the following values in the **volumes** to configure the application - - `/path/to/library`: Path to the directory where your ROM files will be stored (usually the parent folder of the `roms` folder) - - `/path/to/assets`: Path to the directory where you will store your save files and other assets - - `/path/to/config`: Path to the directory where you will store the config.yml (store the `config.yml` file in this folder) -7. Save the file as _docker-compose.yml_ instead of _docker-compose.example.yml_. It should look something like this: - - - ???+ example "Example Docker Compose" - ``` yaml - --8<-- "quick-start.docker-compose.yml" - ``` - -8. Open the terminal and navigate to the directory containing the docker-compose file -9. Run `docker compose up -d` to kick off the docker pull. You will see it pull the container and set up the volumes and network: - - ```asciinema-player - { - "file": "../latest/resources/asciinema/quick-start-docker-compose.cast", - "title": "RomM docker compose install", - "preload": true, - "loop": true, - "auto_play": true, - "cols": 140, - "rows": 30, - "fit": "width", - "terminal_font_size": "small", - "terminal_line_height": "1.2", - "terminal_font_family": "Roboto Mono, Monaco, Consolas, monospace" - } - ``` - -10. Run `docker ps -f name=romm` to verify that the containers are running -11. Open a web browser and navigate to `http://localhost:80`, where you should be greeted with the RomM setup page -12. Go through the setup wizard, setting your admin username and password -13. Log in with the credentials you set in the last step - -## Configure - -### Importing your ROMs via scanner - -This method is generally the fastest and recommended for first time setup. You need your library properly mounted as a container volume: - -1. Log into RomM with your user credentials -2. Click the `Scan` button in the sidebar -3. Select the metadata providers you want to fetch metadata from -4. The system will now begin scanning the ROM files and applying metadata to them. You can click on any of the items that it has tagged to see the metadata it pulled without having to stop the scan -5. After the scan completes, click the RomM logo to go back to the main screen. You should see the platforms and recent games it has scanned. You are now ready to rock with RomM! - -### Uploading your ROMs via Web Interface - -This method is certainly viable, but not recommended if you have a lot of ROMs and/or multiple platforms. It is good for adding files after the fact as your collection grows, but wouldn't be recommended for the first setup, nor for multi-file ROMs: - -1. Log into RomM with your user credentials -2. Click the `Upload` button in the sidebar -3. Select the platform, then click `+ ADD` and select the ROMs you want to upload in the file selector that appears -4. Click `Upload` to begin uploading the ROMs -5. Repeat for all the `roms/platforms` you have - -upload dialog diff --git a/docs/Getting-Started/Reverse-Proxy.md b/docs/Getting-Started/Reverse-Proxy.md deleted file mode 100644 index 90adb9e6..00000000 --- a/docs/Getting-Started/Reverse-Proxy.md +++ /dev/null @@ -1,162 +0,0 @@ - - -Here are some basic configurations for popular reverse proxies. Additional configuration may be required depending on your specific setup. - -## Caddy - -```caddyfile -http://romm.mysite.com { - reverse_proxy romm:8080 -} -``` - -### Caddy + TLS (HTTPS) - -```caddyfile -romm.mysite.com { - encode zstd gzip - - header { - Strict-Transport-Security "max-age=31536000;" - X-XSS-Protection "1; mode=block" - X-Frame-Options "SAMEORIGIN" - X-Robots-Tag "noindex, nofollow" - -Server - -X-Powered-By - } - - reverse_proxy romm:8080 -} -``` - -## Nginx - -```nginx -server { - listen 80 default_server; - server_name romm.mysite.com; - client_max_body_size 0; - - location / { - include /config/nginx/proxy.conf; - include /config/nginx/resolver.conf; - set $upstream_app romm; - set $upstream_port 8080; - set $upstream_proto http; - proxy_pass $upstream_proto://$upstream_app:$upstream_port; - } -} -``` - -### Nginx + TLS (HTTPS) - -```nginx -server { - listen 80 default_server; - server_name _; - return 301 https://$host$request_uri; -} - -server { - listen 443 ssl http2; - listen [::]:443 ssl http2; - - server_name romm.mysite.com; - include /config/nginx/ssl.conf; - client_max_body_size 0; - - location / { - include /config/nginx/proxy.conf; - include /config/nginx/resolver.conf; - set $upstream_app romm; - set $upstream_port 8080; - set $upstream_proto http; - proxy_pass $upstream_proto://$upstream_app:$upstream_port; - - # Hide version - server_tokens off; - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - add_header Referrer-Policy "no-referrer-when-downgrade" always; - } -} -``` - -## Nginx Proxy Manager - -Items marked with ❗ are important to set, as RomM may not work correctly otherwise! - -### ⚡ Details - -- Domain Names: `romm.example.com` (replace example with your own) -- Scheme: `http` -- Forward Hostname/IP: device IP (like 192.168.X.X) -- Forward Port: `8080` -- Cache Assets: `off` -- Block Common Exploits: `on` -- Websockets Support: `on` ❗ -- Access List: - (or configure as needed) - -### 🛡️ SSL - -Strongly recommended for security; enable this if you plan to use HTTPS. - -- SSL Certificate: "Request a new SSL Certificate" -- Force SSL: `on` -- HTTP/2 Support: `on` -- HSTS Enabled: `off` -- Email Address for Let's Encrypt: your email address -- I Agree to the TOS: `on` - -### ⚙️ Advanced - -Custom Nginx Configuration ❗ - -```yaml -proxy_max_temp_file_size 0; -``` - -| Details | SSL | Advanced | -| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | -| ![image](https://github.com/user-attachments/assets/e106a8e9-8b27-41ef-8ba2-d43c3b68b269) | ![image2](https://github.com/user-attachments/assets/6c82c785-792a-410a-80f2-d95839cba47b) | ![image3](https://github.com/user-attachments/assets/566ae834-99b5-42f3-b46b-306b8f73b5b4) | - -## Traefik - -### Using a configuration document - -```yaml -http: - romsdomainse: - entryPoints: - - "https" - rule: "Host(`roms.domain.se`)" - middlewares: - - default-headers - - https-redirectscheme - tls: - certResolver: http - service: romsdomainse - -services: - romsdomainse: - loadBalancer: - servers: - - url: "http://192.168.1.100:8080" - passHostHeader: true -``` - -### Using labels in docker compose - -```yaml -labels: - - "traefik.enable=true" - - "traefik.http.services.romm.loadbalancer.server.port=8080" - - "traefik.http.routers.romm.rule=Host(`romm.YOUR_DOMAIN.com`)" - - "traefik.http.routers.romm.entrypoints=websecure" - - "traefik.http.routers.romm.tls=true" - - "traefik.http.routers.romm.tls.certresolver=https" -``` diff --git a/docs/Integrations/Playnite-plugin.md b/docs/Integrations/Playnite-plugin.md deleted file mode 100644 index 9555dc65..00000000 --- a/docs/Integrations/Playnite-plugin.md +++ /dev/null @@ -1,50 +0,0 @@ - - - -
- romm[playnite] isotipo -
- -Playnite is an open source video game library manager with one simple goal: To provide a unified interface for all of your games. - -This plugin allows you to import your RomM library into Playnite. It queries the RomM API to create Playnite library entires for each of your games. Installing a game in Playnite will download it from RomM and store it on your system, allowing you to launch it in your emulator of choice. - -## Installation - -- Option A: Open this link in your browser to launch Playnite and install the plugin automatically: `playnite://playnite/installaddon/RomM_9700aa21-447d-41b4-a989-acd38f407d9f` -- Option B: Download the plugin from the [Playnite add-ons website](https://playnite.link/addons.html#RomM_9700aa21-447d-41b4-a989-acd38f407d9f) -- Option C: In Playnite, go to `Menu` -> `Add-ons...` -> `Browse` -> `Libraries`, search for `RomM`, and click `Install` -- Option D: Download the latest release from the [releases page](https://github.com/rommapp/playnite-plugin/releases/latest) and install it manually by dragging the `.pext` file onto Playnite - -## Setup - -### Emulators - -The plugin requires that you have **at least 1 emulator installed** on your system and configured in Playnite. You can use a built-in emulator or a custom one. **If no emulators are installed and configured, you won't be able to complete setup!** To set up an emulator, go to `Menu` -> `Library` -> `Configure Emulators...` -> `Add emulator...`. - -### Settings - -The plugin needs to be configured before it can be used. To do this, go to `Menu` -> `Library` -> `Configure Integrations...` -> `RomM`. - -#### Authentication - -You'll need to enter the host URL of your RomM instance, as well as a username and password. Passwords are stored in plaintext in Playnite, so it's recommended to use a separate account with the "VIEWER" role. **The host URL has the include the protocol (http/https) and should not include a trailing slash, e.g. `https://romm.example.com`.** - -#### Emulator path mappings - -| Field | Description | Example | Required | -| ---------------- | ---------------------------------------------------------- | ----------------- | -------- | -| Emulator | A built-in (or custom) emulator | Dolphin | ✓ | -| Emulator Profile | A built-in (or custom) emulator profile | Nintendo GameCube | ✓ | -| Platform | The platform or console | Nintendo GameCube | ✓ | -| Destination Path | The path where downloaded ROMs will be stored | `C:\roms\gc` | ✓ | -| Auto-extract | Whether compressed files should be extracted automatically | | | -| Enabled | Whether the mapping is enabled | | | - -## Importing your library - -Once you've set up the plugin, you can import your library by going to `Menu` -> `Library` -> `Import RomM library`. All games matching the emulator path mappings will be imported into Playnite. - -Installing a game will download it from RomM and store it in the destination path. You can then launch the game from Playnite, and it will be launched using the configured emulator. - -By default, compressed files will be extracted automatically into a folder matching the game's name. You can modify this behavior in the settings page. diff --git a/docs/Integrations/Tinfoil-integration.md b/docs/Integrations/Tinfoil-integration.md deleted file mode 100644 index 0d70a15f..00000000 --- a/docs/Integrations/Tinfoil-integration.md +++ /dev/null @@ -1,58 +0,0 @@ - - - -
- romm[tinfoil] isotipo -
- -Tinfoil is a homebrew application for the Nintendo Switch, primarily used for installing and managing software, including games and updates, that are not obtained through official channels. - -This will help you configure Tinfoil for your Switch to work with your RomM library. - -## Setup - -### Prepare - -Please note down the following in order to make this as smooth as possible, as well as some pre-reqs: - -- RomM updated to at least [3.5.0](https://github.com/rommapp/romm/releases/tag/3.5.0) -- Add `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` to your environment variables and restart the container -- The URL you use to access RomM - - This can either be `http` or `https` - - The system will prefer local access though to avoid reverse proxy issues -- Feed URL: `/api/feeds/tinfoil` -- The username and password you use to login to RomM - -### Configure - -Now it's time to configure your switch - Please follow the steps, this will assume you have Tinfoil installed and know how to use the basic functions of it. - -1. Open Tinfoil and go to File Browser -2. Scroll over to the selection and press - in order to access the new menu. -3. Enter these Options - - Protocol - `http` or `https` depending on your connection - - Host - Host of your RomM instance - - Port - Port of your RomM Instance - - Path - /api/feeds/tinfoil - - Username - Username of your RomM instance - - Password - Password of your RomM instance - - Title - Free text title, make it whatever you want. - - Enabled - Yes -4. Press X to save -5. Now close out of Tinfoil and go back in, so it can scan the TitleIDs - if everything is correct you will have this custom `motd`: - " RomM Switch Library" - -Now you will be able to see the files in "New Games" tab of Tinfoil OR you can access it within the "File Browser" section that you setup earlier. - -![Image of Tinfoil](../resources/tinfoil/tinfoilscreen.jpg) - -### Additional - -It didn't pull anything through to "New Games" and has not parsed any information about the titles?! - -That would be because the filename it has tried to pull had no TitleID (Improvement to RomM coming soon :tm:) - -Make sure the filename has the TitleID within the title like this: -![TitleID](../resources/tinfoil/titleid.jpg) - -Once this is done, the next time Tinfoil is opened it is always parsed and re-scanned. diff --git a/docs/Integrations/muOS-app.md b/docs/Integrations/muOS-app.md deleted file mode 100644 index 59e896ca..00000000 --- a/docs/Integrations/muOS-app.md +++ /dev/null @@ -1,34 +0,0 @@ - - - -
- romm[muos] isotipo -
- -muOS is a Custom Firmware (CFW) primarily for handheld devices. Configurable, themeable, friendly, easy-to-use. - -The muOS app connects to your RomM instance and allows you to fetch games wirelessly from your Anbernic device. - -## Setup - -### muOS - -We leverage the muOS [Archive Manager](https://muos.dev/installation/archive) to install/update the app. - -1. Head to the [latest release](https://github.com/rommapp/muos-app/releases/latest) and download the `RomM.muOS.x.x.x.muxapp` file. -2. Move the muxapp file to `/mnt/mmc/ARCHIVE` on your device. -3. Launch the manager from `Applications > Archive Manager` and select `RomM.muOS.x.x.x.muxapp`. -4. Once installed, make a copy of `/mnt/mmc/MUOS/application/RomM/env.template`, rename it to `/mnt/mmc/MUOS/application/RomM/.env`, edit it (any method is fine, we recommend SSH) and set `HOST`, `USERNAME` and `PASSWORD`. -5. Launch the app from `Applications > RomM` and start browsing your collection. - -### EmulationStation - -We use PortMaster to install the app on devices running EmulationStation. - -1. Download the `RomM App.sh` file and `RomM/` folder to the `roms/ports` on your device. -2. Make the `RomM App.sh` file executable by running `chmod +x RomM App.sh`. -3. Launch EmulationStation and navigate to the `Ports` section. - - -!!! note - Your device must connect to your RomM instance or home server over Wi-Fi. The easiest method is to keep them on the same network and set HOST to the server's IP and the port where RomM is running. Advanced users or those using reverse proxies can configure their network and DNS settings as needed, using a URL with a domain like `https://romm.domain.com`. diff --git a/docs/Integrations/pkgj-integration.md b/docs/Integrations/pkgj-integration.md deleted file mode 100644 index ddbd1cfa..00000000 --- a/docs/Integrations/pkgj-integration.md +++ /dev/null @@ -1,38 +0,0 @@ - - - -pkgj is a homebrew application for the PS Vita, primarily used for installing PS Vita and PSP Games. While its default configuration talks to a set of pre-defined URLs, you can configure it to talk to custom feeds. - -This will help you install PSP or PS Vita games from your RomM library over Wi-Fi - -## Setup - -### Prepare - -Please note down the following in order to make this as smooth as possible, as well as some pre-reqs: - -- Your PS Vita should already have the following installed: - - pkgj - - A method to access files to edit on your Vita, such as VitaShell -- The URL you use to access RomM - - This can either be `http` or `https` -- Feed URLs: - - `/api/feeds/pkgi/psp/game` - - `/api/feeds/pkgi/psp/dlc` - - `/api/feeds/pkgi/psvita/game` - - `/api/feeds/pkgi/psvita/dlc` - -### Configure - -1. Connect your PS Vita to your PC over USB using VitaShell -2. Open `/pkgj/config.txt` in a text editor to add the feed URLs -3. If you have PS Vita games in `.pkg` format, add a `url_games {romm_url}/api/feeds/pkgi/psvita/game` to the end of the file -4. If you have PS Vita DLCs in `.pkg` format, add a `url_dlcs {romm_url}/api/feeds/pkgi/psvita/dlc` to the end of the file -5. If you have PSP games in `.pkg` format, add a `url_psp_games {romm_url}/api/feeds/pkgi/psp/game` to the end of the file -6. If you have PSP DLCs in `.pkg` format, add a `url_psp_dlcs {romm_url}/api/feeds/pkgi/psp/dlc` to the end of the file -7. Save the file and disconnect from VitaShell -8. Open pkgj, Press `△` to open the menu and select "Refresh" - -### Additional - -If, during a Refresh, pkgj presents an error saying "can't get list: list is empty...", check whether you have any Games/DLCs for the feeds you've added in `.pkg` format. RomM won't list any Games in other formats such as `.iso`. diff --git a/docs/Maintenance/Migrating-to-new-machine.md b/docs/Maintenance/Migrating-to-new-machine.md deleted file mode 100644 index d3a31790..00000000 --- a/docs/Maintenance/Migrating-to-new-machine.md +++ /dev/null @@ -1,93 +0,0 @@ - - -## Migrating RomM to a new system - -Migrating RomM to a new system is possible, but all of the docker volumes must be copied for RomM to run correctly. - -Following the the setup in the [Quick Start Guide](https://docs.romm.app/latest/Getting-Started/Quick-Start-Guide/#build) these volumes are created be default - -RomM should be stopped before following this guide. - -```yaml -volumes: - mysql_data: - romm_resources: - romm_redis_data: - -services: - romm: - volumes: - - romm_resources - - romm_redis_data - romm-db: - volumes: - - mysql_data -``` - -These volumes will need to manually moved to the new system. This is a straightforward process that includes determining their location and then copying them. - -### Determining the docker root directory and copying the volumes - -1. First determine the docker root directory - -```bash -docker info | grep 'Docker Root Dir' -``` - -The expected output on a standard linux system: - -```bash -Docker Root Dir: /var/lib/docker -``` - -2. Double check that the volumes have been created by docker and are owned by the docker engine - -```bash -docker volume ls -``` - -Following the default quick start guide the following volumes will have been made - -```bash -DRIVER VOLUME NAME -local romm_mysql_data -local romm_romm_redis_data -local romm_romm_resources -``` - -3. Inspect each volume to get the exact location of the volume data - -```bash -docker volume inspect romm_mysql_data | grep Mountpoint -``` - -- The output of the `docker inspect` will return the exact storage location of the volumes data - -```bash -"Mountpoint": "/var/lib/docker/volumes/romm_mysql_data/_data", -``` - -4. Copy those volumes into a new location so that they can be safely migrated to a new system **each volume needs to be in its own folder** - -```bash -cp -r /var/lib/docker/volumes/romm_mysql_data/_data/ /your/new/path/romm_mysql_data - -cp -r /var/lib/docker/volumes/romm_romm_redis_data/_data /your/new/path/romm_romm_redis_data - -cp -r /var/lib/docker/volumes/romm_romm_resources/_data /your/new/path/romm_romm_resources -``` - -5. Update the `docker-compose.yml` volume paths with the newly copied data to determine RomM still loads correctly. - -```yaml -services: - romm: - volumes: - - /your/new/path/romm_romm_resources # romm_resources - - /your/new/path/romm_romm_redis_data # romm_redis_data - romm-db: - volumes: - - /your/new/path/romm_mysql_data # mysql_data -``` - -If RomM starts up correctly, then it is safe to copy all of your RomM folders to a new system. diff --git a/docs/Maintenance/Scheduled-Tasks.md b/docs/Maintenance/Scheduled-Tasks.md deleted file mode 100644 index 741589d2..00000000 --- a/docs/Maintenance/Scheduled-Tasks.md +++ /dev/null @@ -1,43 +0,0 @@ - - -## Scheduled tasks - -Scheduled tasks can be enabled and configured with the following environment variables: - -| Variable | Description | Value | -| ------------------------------------------ | ------------------------------------------------------------ | :-----------: | -| ENABLE_SCHEDULED_RESCAN | Enable scheduled re-scanning of library | `true` | -| SCHEDULED_RESCAN_CRON | Cron expression for scheduled re-scanning | `"0 3 * * *"` | -| ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB | Enable scheduled updating of Switch TitleDB index | `true` | -| SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON | Cron expression for scheduled updating of Switch TitleDB | `"0 4 * * *"` | -| ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA | Enable scheduled updating of LaunchBox metadata | `true` | -| SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON. | Cron expression for scheduled updating of LaunchBox metadata | `"0 5 * * *"` | - -### Scheduled re-scan - -Users can opt to enable scheduled re-scans, and set the interval using Cron notation. Not that the scan will **not completely re-scan** every file, only catching those which have been added/updated. - -### Switch titleDB update - -Support was added for Nintendo Switch ROMs with filenames using the [titleid/programid format](https://wiki.gbatemp.net/wiki/List_of_Switch_homebrew_titleID) (e.g. 0100000000010000.xci). If a file under the `switch` folder matches the regex, the scanner will use the index to attempt to match it to a game. If a match is found, the IGDB handler will use the matched name as the search term. - -The associated task updates the `/fixtures/switch_titledb.json` file at a regular interval to support new game releases. - -### LaunchBox metadata update - -RomM can also update the LaunchBox metadata database at a regular interval. This is onyl useful if you've enabled the LaunchBox metadata provider. The database is updated by downloading the latest version from the [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) and replacing the existing one. - -## File system watcher - -RomM can also monitor the filesystem for events (files created/moved/deleted) and schedules a re-scan of the platform (or entire library is a new platform was added). - -The watcher can be enabled and configured with the following environment variables: - -| Variable | Description | Value | -| ---------------------------------- | ------------------------------------------------------------------- | :----: | -| ENABLE_RESCAN_ON_FILESYSTEM_CHANGE | Enable re-scanning of library when filesystem changes | `true` | -| RESCAN_ON_FILESYSTEM_CHANGE_DELAY | Delay in minutes before re-scanning library when filesystem changes | `5` | - -The watcher will monitor the `/library/roms` folder for changes to the filesystem, such as files being added, moved or deleted. It will ignore certain events (like modifying the file content or metadata), and will skip default OS files (like `.DS_Store` on mac). - -When a change is detected, a scan will be scheduled for sometime in the future (default 5 minutes). If other events are triggered between now and the time at which the scan starts, more platforms will be added to the scan list (or the scan may switch to a full scan). This is done to reduce the number of tasks scheduled when many big changes happen to the library (mass upload, new mount, etc.) diff --git a/docs/Maintenance/Upgrading-to-3.0.md b/docs/Maintenance/Upgrading-to-3.0.md deleted file mode 100644 index b5fbc8d8..00000000 --- a/docs/Maintenance/Upgrading-to-3.0.md +++ /dev/null @@ -1,61 +0,0 @@ - - -Version 3.0 of RomM introduces a number of breaking changes aimed at improving performance and usability, which will require some users to make specific changes before upgrading to ensure compatibility and to take full advantage of the new features. - -All of the following changes are reflected in the [example docker-compose.yml file](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml), which has been simplified greatly. **Please read this entire file carefully, as failing to do so may cause RomM to become inaccessible or unresponsive.** - -## Dropped support for SQLite - -We're removed support for SQLite as we've faced a number of engineering issues with it in the past, and MariaDB has proven more stable and robust. If you currently use SQLite, we'll automatically migrate your data from SQLite to MariaDB, but you'll **first need to make the following changes before upgrading to the latest image.** - -In your environment variables, change `ROMM_DB_DRIVER` to `mariadb` (or remove it completely as it's no longer needed). You'll then want to add the following environment variables: - -```yaml -- DB_HOST=mariadb -- DB_PORT=3306 -- DB_NAME=romm # Should match MYSQL_DATABASE in mariadb -- DB_USER=romm-user # Should match MYSQL_USER in mariadb -- DB_PASSWD= # Should match MYSQL_PASSWORD in mariadb -``` - -To setup a new MariaDB container, have a look at the [example docker-compose.yml file](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml). - -## Authentication as standard - -To support new features like EmulatorJS and saves/states management, we've decided to require authentication for all users. Anyone currently running RomM with authentication disabled will need to remove the `ROMM_AUTH_ENABLED` environment variable and add the following ones: - -```yaml -- ROMM_AUTH_SECRET_KEY= # Generate a key with `openssl rand -hex 32` -``` - -We understand that this requirement for authentication might conflict with the way some users currently share their collection with others (unrestricted access for all). However, given the exciting new features we've built, and the ones we're looking to build in the near future, we feel this is the right decision for the project. - -## Redis is now built-in - -As Redis is [required for authentication](../Getting-Started/Authentication.md) to work, we've integrated it directly into the docker image. If you're currently running the experimental Redis container, you can remove it, along with these environment variables: - -```yaml -- ENABLE_EXPERIMENTAL_REDIS -- REDIS_HOST -- REDIS_PORT -``` - -## Configuration folder - -Mounting the `config.yml` file is now done by mounting a `config` folder.. Place your existing `config.yml` file inside a folder and bind it to `/romm/config`: - -```yaml -- /path/to/config:/romm/config -``` - -Updated [config.example.yml](https://github.com/rommapp/romm/blob/master/examples/config.example.yml) - -## Support for saves, states and screenshots - -This version introduces preliminary support for uploading/downloading saves, states and screenshots (read more about it in the 3.0 release notes). We've added a new volume mapping for these types of files called `assets`, which you'll want to bind to a local folder (or volume) so they'll persist. In your volumes section, add the following mapping, where `/path/to/assets/` is some folder where you'll want to store these assets (and make sure that folder exists): - -```yaml -- /path/to/assets:/romm/assets -``` - -We recommend creating a folder next to your `library`/the one mapped to `/romm/library` in order to keep all your RomM files in the same place. diff --git a/docs/Miscellaneous/Brand-Guidelines.md b/docs/Miscellaneous/Brand-Guidelines.md deleted file mode 100644 index 8c024baf..00000000 --- a/docs/Miscellaneous/Brand-Guidelines.md +++ /dev/null @@ -1,64 +0,0 @@ - - - -We’ve put together some guidelines for those anyone who wants to use our name and logo. In this context, "RomM", "The RomM Project", "the project", "we", "us", and "our" refer to the RomM project. - -## The Logo - -
- romm isotipo -
-
- -The logo should always be used in its standard colors: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ColorHex Code
#371f69#371f69
#553e98#553e98
#ede5f8#ede5f8
#bea4e1#bea4e1
#e6c7a7#e6c7a7
#e1a38d#e1a38d
-
- -![Branding guidelines](../resources/romm/brand_guidelines/poster.png) - -## Do these things - -- Use our logo to link to any page or site owned or operated by the project. -- Use our logo in a blog post or news article about the project. -- Use our logo to inform others that your project integrates with RomM. -- Always use our logo in the colors provided. -- Always use our name in a way that makes it clear you are not affiliated with the project. - -If you're working on a project that integrates with RomM and would like to use/remix the logo, **please reach out to us first**. We'd love to hear about what you're working on! - -## Please don't do these things - -- Use our name or logo in any way that would suggest you are us, are endorsed by us, or are part of the project. -- Use our name or logo in a way that implies partnership, sponsorship, or endorsement. -- Use our name or logo as the name or logo for your project, product, service, social media account, company, or website. -- Use our name or logo to promote, advertise or sell any private business, closed-source software, commercial product, or paid service. diff --git a/docs/Miscellaneous/FAQs.md b/docs/Miscellaneous/FAQs.md deleted file mode 100644 index 6bb71f5e..00000000 --- a/docs/Miscellaneous/FAQs.md +++ /dev/null @@ -1,17 +0,0 @@ - - -### When will the next version of RomM be available? - -When it's ready. - -### When will the version after that one release? - -After the upcoming version is released. - -### When will X feature be available? - -Sometime between now and the heat death of the universe. - -### When will version `x.x.x` of RomM (or any of the RomM clients/apps/plugins) be released? - -Same as above question. diff --git a/docs/OIDC-Guides/OIDC-Setup-With-Authelia.md b/docs/OIDC-Guides/OIDC-Setup-With-Authelia.md deleted file mode 100644 index 44594913..00000000 --- a/docs/OIDC-Guides/OIDC-Setup-With-Authelia.md +++ /dev/null @@ -1,96 +0,0 @@ -# OIDC Setup With Authelia - -## A quick rundown of the technologies - -### What is Authelia? - -Authelia is an open-source authentication and authorization server providing two-factor authentication and single sign-on (SSO) for your applications via a web portal. It acts as a companion for reverse proxies by allowing, denying, or redirecting requests. Authelia can be deployed alongside your other services to centralize identity management. - -## Setting up a Provider and Application in Authelia - -### Step 1: Install and Configure Authelia - -Before setting up a provider and app, ensure that Authelia is installed and running by following the [getting started](https://www.authelia.com/integration/prologue/get-started/) and [OIDC provider](https://www.authelia.com/configuration/identity-providers/openid-connect/provider/) guides. - -### Step 2: Add a client - -First, in Authelia's `configuration.yml`, at `identity_providers` → `oidc` → `claims_policies` you'll need to add a Claims Policy if you do not already have one with the specified claims: - -```yaml -# identity_providers: -# oidc: -claims_policies: - with_email: # You can name this however you want - id_token: - [ - "email", - "email_verified", - "groups", - "alt_emails", - "preferred_username", - "name", - ] -``` - -To read more about claims_policies and why you need it for RomM, see [this section in the Authelia docs](https://www.authelia.com/integration/openid-connect/openid-connect-1.0-claims/#restore-functionality-prior-to-claims-parameter). - -Then, in the same `configuration.yml`, under `identity_providers` → `oidc` → `clients`, add a new entry: - -- A **random** `client_id` and `client_secret` - - See the [official recommendations](https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret) on how to generate these. -- `public` should be set to `false`. -- `redirect_uris` should include your RomM instance's URL + `/api/oauth/openid` (e.g., `http://romm.host.local/api/oauth/openid`). -- `claims_policy` is the name of the entry at claims_policies that you just added (or already had). -- `scopes` includes `openid`, `email` and `profile`. -- `token_endpoint_auth_method` should be set to `client_secret_basic`. -- `userinfo_signed_response_alg` should be set to `none`. - -Refer to the [official docs](https://www.authelia.com/configuration/identity-providers/openid-connect/clients/) for more details. - -This entry should look like this: - -```yaml -#identity_providers: -# oidc: -# clients: -- client_id: "" # read above for how generate - client_name: "RomM" # will be displayed in Authelia to users - client_secret: "$pbkdf2-sha512$randomly_generated" # read above for how generate - public: false - authorization_policy: "two_factor" # or one_factor, depending on your needs - grant_types: - - authorization_code - redirect_uris: - - "http://romm.host.local/api/oauth/openid" - claims_policy: "with_email" - scopes: - - "openid" - - "email" - - "profile" - - "groups" - userinfo_signed_response_alg: "none" - token_endpoint_auth_method: "client_secret_basic" -``` - -### Step 3: Configure RomM Environment Variables - -To enable OIDC authentication in RomM, you need to set the following environment variables: - -- `OIDC_ENABLED`: Set to `true` to enable OIDC authentication. -- `OIDC_PROVIDER`: The lowercase name of the provider (`authelia`). -- `OIDC_CLIENT_ID`: The client ID copied from the Authelia application. -- `OIDC_CLIENT_SECRET`: The generated output from `Random Password`. -- `OIDC_REDIRECT_URI`: The redirect URI configured in the Authelia provider, in the format `http://romm.host.local/api/oauth/openid`. -- `OIDC_SERVER_APPLICATION_URL`: The base URL for you Authelia instance, e.g. `http://authelia.host.local`. - -### Step 4: Set your Email in RomM - -In RomM, open your user profile and set your email address. This email **has to match** your user email in Authelia. - -![Set email](../resources/authelia/1-user-profile.png) - -### Step 5: Test the Integration - -After configuring the environment variables, restart (or stop and remove) your RomM instance and navigate to the login page. You should see an option to log in using OIDC. Click on the OIDC button, and you'll be redirected to Authelia for authentication. Once authenticated, you'll be redirected back to RomM. - -![Login with OIDC](../resources/authelia/2-romm-login.png) diff --git a/docs/OIDC-Guides/OIDC-Setup-With-Authentik.md b/docs/OIDC-Guides/OIDC-Setup-With-Authentik.md deleted file mode 100644 index f0d045d8..00000000 --- a/docs/OIDC-Guides/OIDC-Setup-With-Authentik.md +++ /dev/null @@ -1,98 +0,0 @@ -# OIDC Setup With Authentik - -## A quick rundown of the technologies - -### What is Authentik? - -Authentik is an open-source identity provider (IdP) designed to manage authentication, authorization, and user management across applications. It supports modern authentication protocols and provides tools to simplify integration, including single sign-on (SSO), multi-factor authentication (MFA), and auditing capabilities. Authentik can be deployed alongside your other services to centralize identity management. - -## Setting up a Provider and Application in Authentik - -### Step 1: Install and Configure Authentik - -Before setting up a provider and app, ensure that Authentik is installed and running by following the [official installation guide.](https://docs.goauthentik.io/docs/install-config/install/docker-compose). - -1. Access Authentik via its web interface. -2. Log in as an administrator. -3. Navigate to the “Admin Interface” to configure the necessary components. - -![Authentik user dashboard](../resources/authentik/1-user-dashboard.png) - -### Step 2: Create a Property Mapping - -In version 2025.10 Authentik changed their default value for the `email_verified` field from true to false. -Since RomM requires a verified email address, without this property, the authentication would fail. - -1. **Navigate to Property Mappings** - - Go to the "Property Mappings" section in the Authentik admin interface `Customization > Property Mappings` -2. **Create a new Property Mapping** - - Select "Scope Mapping" - - Enter a Name like "RomM Email Verification" - - Set `email` as scope name. - - Set the following as the expression: - ```py - return { - "email": user.email, - "email_verified": True, - } - ``` - - It should look like this - ![Propperty Mapping](../resources/authentik/propperty-mapping.png) -3. **Click Create**. - -[Authentik docs reference](https://version-2025-10.goauthentik.io/add-secure-apps/providers/property-mappings/#scope-mappings-with-oauth2) - -### Step 3: Create a Provider - -A provider in Authentik acts as the bridge between RomM and Authentik. - -1. **Navigate to Providers**: - - Go to the "Providers" section in the Authentik admin interface. -2. **Create a New Provider**: - - Click on “Create” and choose the protocol (e.g., “OIDC Provider”). - ![Create a new provider](../resources/authentik/2-create-provider.png) -3. **Select "OAuth2/OpenID Provider"** - ![Select OAuth2 provider](../resources/authentik/3-new-provider.png) -4. **Configure Provider Settings**: - - **Name**: Assign a unique name to the provider (e.g., "RomM OIDC Provider"). - - **Authorization flow**: Select **implicit consent**. - - **Redirect URIs**: Add your RomM instance's URL + `/api/oauth/openid` (e.g., `http://romm.host.local/api/oauth/openid`). -5. **Copy the Client ID and Secret**: - - You'll need these to set `OIDC_CLIENT_ID` and `OIDC_CLIENT_SECRET` in your RomM instance. - ![Provider settings](../resources/authentik/4-provider-secrets.png) -6. **Click Create**. - -### Step 3: Register an Application - -An app in Authentik represents the external service (in our case RomM) that will use the provider for authentication. - -1. **Navigate to Applications**: - - Go to the "Applications" section in the admin interface. - ![Applications](../resources/authentik/5-applications.png) -2. **Create a New Application**: - - Click on “Create” and configure the app settings: - **Name**: Provide a recognizable name (e.g., "RomM"). - **Slug**: Create a unique identifier for the app (e.g., `romm`). - **Provider**: Link the app to the previously created provider, "RomM OIDC Provider". - ![New application](../resources/authentik/6-new-application.png) -3. **Click Create**. - -### Step 4: Configure RomM Environment Variables - -To enable OIDC authentication in RomM, you need to set the following environment variables: - -- `OIDC_ENABLED`: Set to `true` to enable OIDC authentication. -- `OIDC_PROVIDER`: The lowercase name of the provider (`authentik`). -- `OIDC_CLIENT_ID`: The client ID copied from the Authentik application. -- `OIDC_CLIENT_SECRET`: The client secret copied from the Authentik application. -- `OIDC_REDIRECT_URI`: The redirect URI configured in the Authentik provider, in the format `http://romm.host.local/api/oauth/openid`. -- `OIDC_SERVER_APPLICATION_URL`: The URL of the Authentik application, e.g., `http://authentik.host.local/application/o/romm`. - -### Step 5: Set your Email in RomM - -In RomM, open your user profile and set your email address. This email **has to match** your user email in Authentik. - -![Set email](../resources/authentik/7-user-profile.png) - -### Step 6: Test the Integration - -After configuring the environment variables, restart (or stop and remove) your RomM instance and navigate to the login page. You should see an option to log in using OIDC. Click on the OIDC button, and you'll be redirected to Authentik for authentication. Once authenticated, you'll be redirected back to RomM. - -![Login with OIDC](../resources/authentik/8-romm-login.png) diff --git a/docs/OIDC-Guides/OIDC-Setup-With-Keycloak.md b/docs/OIDC-Guides/OIDC-Setup-With-Keycloak.md deleted file mode 100644 index d4becc0f..00000000 --- a/docs/OIDC-Guides/OIDC-Setup-With-Keycloak.md +++ /dev/null @@ -1,67 +0,0 @@ -# OIDC Setup With Keycloak - -## A quick rundown of the technologies - -### What is Keycloak? - -Keycloak is an open-source Identity and Access Management solution that provides single sign-on (SSO), OpenID Connect (OIDC), OAuth2, amongst other protocols. - -## Setting up a Provider and Application in Keycloak - -### Step 1: Install or access Keycloak - -Before setting up the OIDC client, ensure that Keycloak is installed and running by following the [setup guide](https://www.keycloak.org/getting-started). - -Log into the Admin Console and either create a new realm for RomM or reuse an existing one. - -### Step 2: Add a client - -1. In the Admin Console select your realm → **Clients** → **Create client**. -2. Leave `Client type` as `OpenID Connect` and enter a `Client ID` (for example `romm`). Click **Next**. -3. On the next page: - - Enable **Client authentication**. - - Leave only the **Standard flow** option enabled. - - Click **Next**. -4. Set the following URLs: - - **Root URL**: `http://romm.host.local` (replace with your RomM URL) - - **Valid Redirect URIs**: `http://romm.host.local/api/oauth/openid` (replace with your RomM URL) - - **Web origins**: `http://romm.host.local` (replace with your RomM URL) -5. Go to the **Credentials** tab and copy the **Client Secret** — you'll need this for the RomM configuration. - -### Step 3: Configure RomM Environment Variables - -To enable OIDC authentication in RomM, you need to set the following environment variables: - -- `OIDC_ENABLED`: Set to `true` to enable OIDC authentication. -- `OIDC_PROVIDER`: The lowercase name of the provider (`keycloak`). -- `OIDC_CLIENT_ID`: The client ID copied from the Keycloak application. -- `OIDC_CLIENT_SECRET`: The generated output from `Random Password`. -- `OIDC_REDIRECT_URI`: The redirect URI configured in the Keycloak provider, in the format `http://romm.host.local/api/oauth/openid`. -- `OIDC_SERVER_APPLICATION_URL`: The base URL for you Keycloak instance including the realm name, e.g. `http://keycloak.host.local/realms/`. - -### Step 5: Set your Email in RomM - -In RomM, open your user profile and set your email address. This email **has to match** your user email in Keycloak. - -Open the Keycloak Admin Console → Users and mark each RomM user's email as verified. Users without verified emails will not be able to log in. - -### Step 6: Test the Integration - -After configuring the environment variables, restart (or stop and remove) your RomM instance and navigate to the login page. You should see the option "LOGIN WITH KEYCLOAK". Click on it and you'll be redirected to Keycloak for authentication. Once authenticated, you'll be redirected back to RomM. - -Note that if the user already exists in RomM, they will be logged in with their existing account and permissions. If it's a new user, an account will be created for them with viewer permissions by default. To change the permissions for new users, see Step 8 below. - -### Step 7: (Optional) Disable password logins - -If you want to enforce OIDC logins and disable password-based logins, set the environment variable `PASSWORD_AUTH_ENABLED` to `false`. This will hide the password login option on the login page, ensuring that all users must authenticate via Keycloak. - -### Step 8: (Optional) Configure permissions for new users - -By default, new users logging in via OIDC will be created with viewer permissions. If you want to change this default behavior, you can set the environment variables: - -- `OIDC_CLAIM_ROLES`: Set to the name of the claim that contains the user's role -- `OIDC_ROLE_VIEWER`: The value of the role claim that maps to viewer permissions -- `OIDC_ROLE_EDITOR`: The value of the role claim that maps to editor permissions -- `OIDC_ROLE_ADMIN`: The value of the role claim that maps to admin permissions - -Configure Keycloak to include the appropriate role claim in the token diff --git a/docs/OIDC-Guides/OIDC-Setup-With-PocketID.md b/docs/OIDC-Guides/OIDC-Setup-With-PocketID.md deleted file mode 100644 index 3d65525c..00000000 --- a/docs/OIDC-Guides/OIDC-Setup-With-PocketID.md +++ /dev/null @@ -1,52 +0,0 @@ -# OIDC Setup With Pocket ID - -## A quick rundown of the technologies - -### What is Pocket ID? - -Pocket ID is a simple OIDC provider that allows users to authenticate with their passkeys to your services. - -The goal of Pocket ID is to be a simple and easy-to-use. There are other self-hosted OIDC providers like Keycloak or ORY Hydra but they are often too complex for simple use cases. - -Additionally, what makes Pocket ID special is that it only supports passkey authentication, which means you don’t need a password. - -## Setting up a client in Pocket ID - -### Step 1: Install and Configure Pocket ID - -Before setting up the OIDC client, ensure that Pocket ID is installed and running by following the [setup guide](https://github.com/stonith404/pocket-id#setup). - -### Step 2: Add a client - -Once you have logged in and configured a PassKey you now need to create an OIDC client, this will let Pocket ID know about the application that needs to be configured, and will give you the relevant keys to add to the RomM compose file. - -- Make sure within Application Configuration that "Emails Verified" is ticked, most OIDC apps will need this. -- Go to OIDC Client -- Click Add OIDC Client - - Name: RomM - - Callback URLs: `https://{host}/api/oauth/openid` -- Click Save -- Stay on this page, you will be shown your client secret only THIS time. - -### Step 3: Configure RomM Environment Variables - -To enable OIDC authentication in RomM, you need to set the following environment variables: - -- `OIDC_ENABLED`: Set to `true` to enable OIDC authentication. -- `OIDC_PROVIDER`: The lowercase name of the provider (`pocket-id`). -- `OIDC_CLIENT_ID`: The client ID copied from the Pocket ID application -- `OIDC_CLIENT_SECRET`: The client secret that is showing within your Pocket ID application. -- `OIDC_REDIRECT_URI`: The redirect URI configured in the Pocket ID provider, in the format `https://{host}/api/oauth/openid`. -- `OIDC_SERVER_APPLICATION_URL`: The authorization URL for you Pocket ID instance, e.g. `https://id.host.local`. - -### Step 4: Set your Email in RomM - -In RomM, open your user profile and set your email address. This email **has to match** your user email in Pocket ID. - -![Set email](../resources/authelia/1-user-profile.png) - -### Step 5: Test the Integration - -After configuring the environment variables, restart (or stop and remove) your RomM instance and navigate to the login page. You should see an option to log in using OIDC. Click on the OIDC button, and you'll be redirected to Pocket ID for authentication. Once authenticated, you'll be redirected back to RomM. - -![Login with OIDC](../resources/pocketid/2-romm-login.png) diff --git a/docs/OIDC-Guides/OIDC-Setup-With-Zitadel.md b/docs/OIDC-Guides/OIDC-Setup-With-Zitadel.md deleted file mode 100644 index d4ad92b2..00000000 --- a/docs/OIDC-Guides/OIDC-Setup-With-Zitadel.md +++ /dev/null @@ -1,82 +0,0 @@ -# OIDC Setup With Zitadel - -## A quick rundown of the technologies - -### What is Zitadel - -Zitadel is an enterprise-grade, open-source identity and access management (IAM) platform that supports OAuth2, OpenID Connect, SAML, and passwordless authentication. It's used to manage users, roles, and secure login for web and cloud applications. - -## Setting up a client in Zitadel - -### Step 1: Install and Configure Zitadel - -Before setting up the OIDC client, ensure that Zitadel is installed and running by following the [setup guide](https://zitadel.com/docs/self-hosting/deploy/overview). - -### Step 2: Create a Project - -Once you have logged in and changed the default password for your Zitadel organization, create a new Project (i.e Romm). This will be the basic settings for roles and authorization. - -In the "General" tab, there are options to allow the following: - -- Assert Roles on Authentication - -Unnecessary: Romm (at this time) does not allow granting permissions based on role, everyone gets viewer and will have to be changed manually using an admin account if desired. - -- Check authorization on Authentication - -Recommended: If you allow registration to your platform, then anyone who registers can instantly access Romm (although only as a viewer, which may not be a problem for some) - -- Check for Project on Authentication - -Optional: It could be used if you plan on separating users by organizations for other applications, but creating separate organizations is not typically useful for general self-hosting purposes - -### Step 2.5 (Optional: If you enabled "Check authorization on Authentication"): Grant user(s) access to the Project - -Click on the Authorization tab and click New. - -Enter the user(s) and click Continue - -It should say "No role has been created yet.", but this is fine, you can just click Save and it should bring you back to the Authorization page with your user(s) listed with no roles - -### Step 3: Create the application - -On the General tab, click the New button under Applications. - -(Check "I'm a pro. Skip this wizard." to enter the information quicker) - -- `Name`: RomM (or whatever you want) -- `Application Type`: Web -- `Grant Types`: Authorization Code -- `Response Types`: Code -- `Authentication Method`: Basic -- `Redirect URIs`: `https://romm.domain.com/api/oauth/openid` -- `Post Logout URIs`: `https://romm.domain.com/` - -Click Create. - -- Stay on this page or copy these down elsewhere, the secret will only show **this one time** - -### Step 3: Configure RomM Environment Variables - -To enable OIDC authentication in RomM, you need to set the following environment variables: - -- `OIDC_ENABLED`: Set to `true` to enable OIDC authentication. -- `OIDC_PROVIDER`: The name of the provider `Zitadel`. -- `OIDC_CLIENT_ID`: The client ID copied from the Zitadel application -- `OIDC_CLIENT_SECRET`: The client secret generated from the Zitadel application -- `OIDC_REDIRECT_URI`: The redirect URI configured in Zitadel `https://rom.domain.com/api/oauth/openid`. -- `OIDC_SERVER_APPLICATION_URL`: The domain for your Zitadel instance `https://zitadel.domain.com`. (The discovery URL for Zitadel is on the basedomain under /.well-known/openid-configuration) - -### Step 4: Enable claims from ID Token (this resolves the "Email is missing from token" error) - -Click close to finish creating the application and then go to the Token Settings tab. - -Check "User Info inside ID Token" and click Save - -### Step 5: Set your Email in RomM - -For your existing RomM admin account, open your user profile on Zitadel and set your email address. This email **has to match** your user email in Zitadel. - -### Step 6: Test the Integration - -After configuring the environment variables, restart (or stop and remove) your RomM instance and navigate to the login page. You should see the option "LOGIN WITH ZITADEL". Click on it and you'll be redirected to Zitadel for authentication. Once authenticated, you'll be redirected back to RomM. diff --git a/docs/Platforms-and-Players/Custom-Platforms.md b/docs/Platforms-and-Players/Custom-Platforms.md deleted file mode 100644 index 7566bbdd..00000000 --- a/docs/Platforms-and-Players/Custom-Platforms.md +++ /dev/null @@ -1,12 +0,0 @@ - - - -While RomM supports every platform listed in the [Supported Platforms page](../Platforms-and-Players/Supported-Platforms.md), the list is not exhaustive, and you may have ROMs in your library for other platforms. To load those files into RomM, place them in a folder for each platform, and give it a name that's **all lowercase**, with **`-` to separate words**, and with **no white spaces**. For example, `pocket-challenge-v2` would map to `Pocket Challenge V2`, and display the default platform icon in the app. - -Furthermore, only a portion of the supported platforms have custom icons built-in. If your library has platforms that aren't listed in [the platforms icons list](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms), RomM will display a default fallback icon. - -If you'd like to load your own custom icons for missing platforms, you can mount `/var/www/html/assets/platforms` to some local folder and place all of your custom **`.ico`** platform icons in there. You'll also want to download the ones [provided in this project](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms) and place them in the same folder. If you'd like to use your own icons for platforms already supported by RomM, just replace the file with another using the exact same name. - -**The name of the `.ico` file should match the slug of the platform on IGDB.** For example, the URL for the AmstradCPC is , so the filename should be `acpc.ico`. - -Screenshot 2023-09-15 at 10 45 04 AM diff --git a/docs/Platforms-and-Players/EmulatorJS-Player.md b/docs/Platforms-and-Players/EmulatorJS-Player.md deleted file mode 100644 index 7f2cd198..00000000 --- a/docs/Platforms-and-Players/EmulatorJS-Player.md +++ /dev/null @@ -1,118 +0,0 @@ - - -[EmulatorJS](https://emulatorjs.org/) is a web-based emulator for various system; that is, it allows you to run old games in your web browser. It's based on [`RetroArch`](https://www.retroarch.com) compiled with [`Emscripten`](https://emscripten.org/), which is a toolchain for compiling C and C++ code to WebAssembly. - - -!!! warning - - Emulation is a complex and resource-intensive process. As such, it may not work well in all browser, especially older or less powerful ones. If you're having trouble running a game, try using a different browser or device. - - PSP emulation with the `ppsspp` core and MS-DOS with the `dosbox-pure` core are not currently supported when using the Console mode. - - -!!! note - Some platforms may require multiple BIOS/firmware files to be loaded at the same time. To do this, create a **ZIP archive** containing all the firmware files for the emulator you've selected and upload it to the **firmware** section of the platform. This ZIP file will be recognized by EmulatorJS as the firmware bundle for the platform. Refer to the [EmulatorJS documentation](https://emulatorjs.org/docs/systems/) for the required list of files for each platform. - -### Saves and states - -Our integration with EmulatorJS automates the process of loading and save files and save states. Before starting the game, select a save and/or state file to load (if one is available). Anytime you manually save the game (or create a save state) by clicking the save or "save and quit" buttons, the save and state files stored with RomM will be updated, so there's no need to manually download or upload them. - -### Netplay - - -!!! warning - Netplay is currently not available. We are looking to bring it back at a later date. If you are an existing Netplay user, please disable this feature. Leaving it on will result in `failed to start game` errors and other intermittent issues. - -Netplay lets you play with friends remotely, in realtime with the build-in web player. As it emulates playing on the same console with two controllers while streaming the video to players 2+, it's best for 2-player, co-op, turn based and party games. - -Start by enabling netplay in your `config.yml`: - -```yaml -emulatorjs: - netplay: - enabled: true -``` - -If you require ICE servers for NAT traversal, we recommend a free-tier [Metered](https://www.metered.ca/stun-turn) account. Create new "TURN Credentials" and replace `` and `` with the entries under "Show ICE Server Array": - -```yaml -emulatorjs: - netplay: - ice_servers: - - urls: "stun:stun.relay.metered.ca:80" - - urls: "stun:stun.relay.metered.ca:80" - - urls: "turn:global.relay.metered.ca:80" - username: "" - credential: "" - - urls: "turn:global.relay.metered.ca:80?transport=tcp" - username: "" - credential: "" - - urls: "turn:global.relay.metered.ca:443" - username: "" - credential: "" - - urls: "turns:global.relay.metered.ca:443?transport=tcp" - username: "" - credential: "" -``` - -Alternatively, use the free STUN servers from Google and TURN servers via the OpenRelayProject: - -```yaml -emulatorjs: - netplay: - ice_servers: - - urls: "stun:stun.l.google.com:19302" - - urls: "stun:stun1.l.google.com:19302" - - urls: "stun:stun2.l.google.com:19302" - - urls: "turn:openrelay.metered.ca:80" - username: "openrelayproject" - credential: "openrelayproject" - - urls: "turn:openrelay.metered.ca:443" - username: "openrelayproject" - credential: "openrelayproject" -``` - -To host a game, start it, then hit the 🌐 icon in botton bar. Set your name, create a room (password optional), and other players should be able to see and join your room. **All players need access to your RomM server to join a room and play together.** - - -!!! note - When netplay is enabled, EmulatorJS loads some assets (including localization files) from the nightly CDN (`https://cdn.emulatorjs.org/nightly/...`). This differs from stable mode, which uses local/bundled assets. Occasional temporary issues (e.g., 404 errors or untranslated UI elements) can occur if the nightly CDN has mismatches, but these usually resolve with the next EmulatorJS stable release integrated into RomM. - -### Supported systems - -Note that only the following systems are currently supported: - -- 3DO -- Amiga -- Arcade/MAME -- Atari 2600 -- Atari 5200 -- Atari 7800 -- Atari Jaguar -- Atari Lynx -- Commodore 64 -- ColecoVision -- DOOM -- Neo Geo Pocket -- Neo Geo Pocket Color -- MS-DOS -- Nintendo 64 -- Nintendo Entertainment System (NES) -- Nintendo Family Computer (Famicom) -- Nintendo DS -- Game Boy -- Game Boy Color -- Game Boy Advance -- PC-FX -- PlayStation (PS) -- PlayStation Portable (PSP) -- Sega 32X -- Sega CD -- Sega Game Gear -- Sega Master System -- Sega Genesis/Megadrive -- Sega Saturn -- Super Nintendo Entertainment System (SNES) -- Super Famicom -- TurboGraphx-16/PC Engine -- Virtual Boy -- WonderSwan -- WonderSwan Color diff --git a/docs/Platforms-and-Players/MS-DOS.md b/docs/Platforms-and-Players/MS-DOS.md deleted file mode 100644 index 17c60e46..00000000 --- a/docs/Platforms-and-Players/MS-DOS.md +++ /dev/null @@ -1,602 +0,0 @@ - - - -[DOS](https://github.com/schellingb/dosbox-pure) is now supported in versions of RomM 4.0 and above thanks to the EmulatorJS player - Simply create a DOS platform to enable the integration - - -!!! info - I highly suggest you upload the games as .zip as the core can take advantage of unzipping and auto mounting options which are explained more below. - - -!!! info - Loading and saving states ARE supported so it's possible you only need to do the below steps once to load the game. - -#### Running Games - -Once you have the play button in the platform available for you there is some additional work you need to do in order to get the games playable and running. You first need to identify what DOS game you are trying to run, there would be three categories - -- Homebrew - - Made by an indie dev, usually just an .exe file which will work fine in DOS once mounted. -- Demo - - Most sites which offer DOS games are shareware demo versions, these function similiar to homebrew and will have all the files needed within the folder. -- Retail - - These will need the CD mounted alongside the game files in order to play the games, this can get tricky and will be a unique per game basis but once you have cracked it you won't need to modify it again. - -The official method to run the games from the EmulatorJS dev is the following (Only works for Homebrew and Demos): - -- Select commandline from the initial loading screen -- `mount A / -t floppy` - This will mount the location of the files -- `A:` - This will take you to the location of the files -- `dir` - to find the .EXE file -- `filename.exe` - This will run the .exe and run the game, you might need some additional configuration but that is purely on the dosbox side and you might need to run the setup.exe file or a file name similiar. - -#### Advanced Running Games - - -!!! warning - This is not for the faint of heart and will require a lot of trial and error. - -As the system is using DOSBOX pure it has a neat trick where it will run .CONF files it finds and automatically mount locations and run .exe files automatically, at the minute this is highly experimental and might be more effort then it is worth, but if you want the files running perfectly then I would suggest you look into this method, but it is extremely trail and error. - -When you run a game in DOSBOX Pure, before it runs and mounts anything it will look for a .conf file and follow those instructions, this way we can actually auto mount locations, mount the required CDs and play the game without typing anything, you just click play, the auto mount does everything in the background and you are presented with the game. - -This is an example using the doom shareware file which has all the files in the folder. - -- Check the folder and make sure there is an .exe file for the game, make sure there is no `*.ins`, `*.cue` or `*.bin` files, if these exist it usually means this is a CD required game and these instructions will not work -- Create a new text document named the same as the .exe (DOOM.conf) and add the following information: - -
- -DOOM.conf Example - -```shell -# This is the configurationfile for DOSBox 0.74. (Please use the latest version of DOSBox) -# Lines starting with a # are commentlines and are ignored by DOSBox. -# They are used to (briefly) document the effect of each option. - -[sdl] -# fullscreen: Start dosbox directly in fullscreen. (Press ALT-Enter to go back) -# fulldouble: Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox. -# fullresolution: What resolution to use for fullscreen: original or fixed size (e.g. 1024x768). -# Using your monitor's native resolution with aspect=true might give the best results. -# If you end up with small window on a large screen, try an output different from surface. -# windowresolution: Scale the window to this size IF the output device supports hardware scaling. -# (output=surface does not!) -# output: What video system to use for output. -# Possible values: surface, overlay, opengl, openglnb, ddraw. -# autolock: Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock) -# sensitivity: Mouse sensitivity. -# waitonerror: Wait before closing the console if dosbox has an error. -# priority: Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized. -# pause is only valid for the second entry. -# Possible values: lowest, lower, normal, higher, highest, pause. -# mapperfile: File used to load/save the key/event mappings from. Resetmapper only works with the defaul value. -# usescancodes: Avoid usage of symkeys, might not work on all operating systems. - -fullscreen=TRUE -fulldouble=false -fullresolution=Fixed -windowresolution=1280x800 -output=direct3d -autolock=true -sensitivity=100 -waitonerror=true -priority=higher,normal -mapperfile=mapper-0.74.map -usescancodes=true - -[dosbox] -# language: Select another language file. -# machine: The type of machine tries to emulate. -# Possible values: hercules, cga, tandy, pcjr, ega, vgaonly, svga_s3, svga_et3000, svga_et4000, svga_paradise, vesa_nolfb, vesa_oldvbe. -# captures: Directory where things like wave, midi, screenshot get captured. -# memsize: Amount of memory DOSBox has in megabytes. -# This value is best left at its default to avoid problems with some games, -# though few games might require a higher value. -# There is generally no speed advantage when raising this value. - -language= -machine=svga_s3 -captures=.\Captures\ -memsize=16 - -[render] -# frameskip: How many frames DOSBox skips before drawing one. -# aspect: Do aspect correction, if your output method doesn't support scaling this can slow things down!. -# scaler: Scaler used to enlarge/enhance low resolution modes. -# If 'forced' is appended, then the scaler will be used even if the result might not be desired. -# Possible values: none, normal2x, normal3x, advmame2x, advmame3x, advinterp2x, advinterp3x, hq2x, hq3x, 2xsai, super2xsai, supereagle, tv2x, tv3x, rgb2x, rgb3x, scan2x, scan3x. - -frameskip=0 -aspect=false -scaler=normal3x - -[cpu] -# core: CPU Core used in emulation. auto will switch to dynamic if available and appropriate. -# Possible values: auto, dynamic, normal, simple. -# cputype: CPU Type used in emulation. auto is the fastest choice. -# Possible values: auto, 386, 386_slow, 486_slow, pentium_slow, 386_prefetch. -# cycles: Amount of instructions DOSBox tries to emulate each millisecond. -# Setting this value too high results in sound dropouts and lags. -# Cycles can be set in 3 ways: -# 'auto' tries to guess what a game needs. -# It usually works, but can fail for certain games. -# 'fixed #number' will set a fixed amount of cycles. This is what you usually need if 'auto' fails. -# (Example: fixed 4000). -# 'max' will allocate as much cycles as your computer is able to handle. -# -# Possible values: auto, fixed, max. -# cycleup: Amount of cycles to decrease/increase with keycombo.(CTRL-F11/CTRL-F12) -# cycledown: Setting it lower than 100 will be a percentage. - -core=auto -cputype=auto -cycles=max -cycleup=10 -cycledown=20 - -[mixer] -# nosound: Enable silent mode, sound is still emulated though. -# rate: Mixer sample rate, setting any device's rate higher than this will probably lower their sound quality. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# blocksize: Mixer block size, larger blocks might help sound stuttering but sound will also be more lagged. -# Possible values: 1024, 2048, 4096, 8192, 512, 256. -# prebuffer: How many milliseconds of data to keep on top of the blocksize. - -nosound=false -rate=22050 -blocksize=2048 -prebuffer=10 - -[midi] -# mpu401: Type of MPU-401 to emulate. -# Possible values: intelligent, uart, none. -# mididevice: Device that will receive the MIDI data from MPU-401. -# Possible values: default, win32, alsa, oss, coreaudio, coremidi, none. -# midiconfig: Special configuration options for the device driver. This is usually the id of the device you want to use. -# See the README/Manual for more details. - -mpu401=intelligent -mididevice=default -midiconfig= - -[sblaster] -# sbtype: Type of Soundblaster to emulate. gb is Gameblaster. -# Possible values: sb1, sb2, sbpro1, sbpro2, sb16, gb, none. -# sbbase: The IO address of the soundblaster. -# Possible values: 220, 240, 260, 280, 2a0, 2c0, 2e0, 300. -# irq: The IRQ number of the soundblaster. -# Possible values: 7, 5, 3, 9, 10, 11, 12. -# dma: The DMA number of the soundblaster. -# Possible values: 1, 5, 0, 3, 6, 7. -# hdma: The High DMA number of the soundblaster. -# Possible values: 1, 5, 0, 3, 6, 7. -# sbmixer: Allow the soundblaster mixer to modify the DOSBox mixer. -# oplmode: Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'. -# Possible values: auto, cms, opl2, dualopl2, opl3, none. -# oplemu: Provider for the OPL emulation. compat might provide better quality (see oplrate as well). -# Possible values: default, compat, fast. -# oplrate: Sample rate of OPL music emulation. Use 49716 for highest quality (set the mixer rate accordingly). -# Possible values: 44100, 49716, 48000, 32000, 22050, 16000, 11025, 8000. - -sbtype=sb16 -sbbase=220 -irq=7 -dma=1 -hdma=5 -sbmixer=true -oplmode=auto -oplemu=default -oplrate=44100 - -[gus] -# gus: Enable the Gravis Ultrasound emulation. -# gusrate: Sample rate of Ultrasound emulation. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# gusbase: The IO base address of the Gravis Ultrasound. -# Possible values: 240, 220, 260, 280, 2a0, 2c0, 2e0, 300. -# gusirq: The IRQ number of the Gravis Ultrasound. -# Possible values: 5, 3, 7, 9, 10, 11, 12. -# gusdma: The DMA channel of the Gravis Ultrasound. -# Possible values: 3, 0, 1, 5, 6, 7. -# ultradir: Path to Ultrasound directory. In this directory -# there should be a MIDI directory that contains -# the patch files for GUS playback. Patch sets used -# with Timidity should work fine. - -gus=false -gusrate=44100 -gusbase=240 -gusirq=5 -gusdma=3 -ultradir=C:\ULTRASND - -[speaker] -# pcspeaker: Enable PC-Speaker emulation. -# pcrate: Sample rate of the PC-Speaker sound generation. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# tandy: Enable Tandy Sound System emulation. For 'auto', emulation is present only if machine is set to 'tandy'. -# Possible values: auto, on, off. -# tandyrate: Sample rate of the Tandy 3-Voice generation. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# disney: Enable Disney Sound Source emulation. (Covox Voice Master and Speech Thing compatible). - -pcspeaker=true -pcrate=44100 -tandy=auto -tandyrate=44100 -disney=true - -[joystick] -# joysticktype: Type of joystick to emulate: auto (default), none, -# 2axis (supports two joysticks), -# 4axis (supports one joystick, first joystick used), -# 4axis_2 (supports one joystick, second joystick used), -# fcs (Thrustmaster), ch (CH Flightstick). -# none disables joystick emulation. -# auto chooses emulation depending on real joystick(s). -# (Remember to reset dosbox's mapperfile if you saved it earlier) -# Possible values: auto, 2axis, 4axis, 4axis_2, fcs, ch, none. -# timed: enable timed intervals for axis. Experiment with this option, if your joystick drifts (away). -# autofire: continuously fires as long as you keep the button pressed. -# swap34: swap the 3rd and the 4th axis. can be useful for certain joysticks. -# buttonwrap: enable button wrapping at the number of emulated buttons. - -joysticktype=fcs -timed=true -autofire=false -swap34=false -buttonwrap=false - -[serial] -# serial1: set type of device connected to com port. -# Can be disabled, dummy, modem, nullmodem, directserial. -# Additional parameters must be in the same line in the form of -# parameter:value. Parameter for all types is irq (optional). -# for directserial: realport (required), rxdelay (optional). -# (realport:COM1 realport:ttyS0). -# for modem: listenport (optional). -# for nullmodem: server, rxdelay, txdelay, telnet, usedtr, -# transparent, port, inhsocket (all optional). -# Example: serial1=modem listenport:5000 -# Possible values: dummy, disabled, modem, nullmodem, directserial. -# serial2: see serial1 -# Possible values: dummy, disabled, modem, nullmodem, directserial. -# serial3: see serial1 -# Possible values: dummy, disabled, modem, nullmodem, directserial. -# serial4: see serial1 -# Possible values: dummy, disabled, modem, nullmodem, directserial. - -serial1=dummy -serial2=dummy -serial3=disabled -serial4=disabled - -[dos] -# xms: Enable XMS support. -# ems: Enable EMS support. -# umb: Enable UMB support. -# keyboardlayout: Language code of the keyboard layout (or none). - -xms=true -ems=true -umb=true -keyboardlayout=auto - -[ipx] -# ipx: Enable ipx over UDP/IP emulation. - -ipx=false - -[autoexec] -# Lines in this section will be run at startup. -# You can put your MOUNT lines here. - -@echo off -Mount C ".." -C: -cls -DOOM.exe -:exit -exit -``` - -
- -- Most of the file is explained but to go into the nitty gritty of the [autoexec] it will mount the local location as C:, it changes to C: it will clear the screen and automatically run the DOOM.exe executable, and will exit the previous shell, meaning it will run the game by just clicking play. -- Zip up the folder and add it to RomM's DOS platform, then just simply click play. If you hit a blank screen it means that something is wrong with the auto exec, and will need manual troubleshooting. - -### Advanced Running Retail Games - - -!!! info - At the minute DOS games redesigned by GOG are NOT supported, this is due to how they mount and use the locations. I am looking into how I can figure this out but I have had a 100% failure rate from the GOG DOS Games. - -Retail games usually require to run alongside a disk even if the game has been "installed" locally. I will use Dungeon Keeper Gold as an example for a retail game with a disc and the configuration needed. - -- Prepare the files like you did before, but this time with the disk images (`*.bin` & `*.cue`) and move them to a folder named CD within the folder. Only within this folder you should all the .bin files and the "matching" .cue sheet (remember .cue is just a text document pulling all these files together, like a playlist) -- Have a look at the KEEPER.cfg file, this file will say how the game was installed and the path it expects the installed files to be at. -- Create a new .conf document named the same as the .exe for me that would be KEEPER.conf - -
- -KEEPER.conf Example - -```shell -# This is the configurationfile for DOSBox 0.74. (Please use the latest version of DOSBox) -# Lines starting with a # are commentlines and are ignored by DOSBox. -# They are used to (briefly) document the effect of each option. - -[sdl] -# fullscreen: Start dosbox directly in fullscreen. (Press ALT-Enter to go back) -# fulldouble: Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox. -# fullresolution: What resolution to use for fullscreen: original or fixed size (e.g. 1024x768). -# Using your monitor's native resolution with aspect=true might give the best results. -# If you end up with small window on a large screen, try an output different from surface. -# windowresolution: Scale the window to this size IF the output device supports hardware scaling. -# (output=surface does not!) -# output: What video system to use for output. -# Possible values: surface, overlay, opengl, openglnb, ddraw. -# autolock: Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock) -# sensitivity: Mouse sensitivity. -# waitonerror: Wait before closing the console if dosbox has an error. -# priority: Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized. -# pause is only valid for the second entry. -# Possible values: lowest, lower, normal, higher, highest, pause. -# mapperfile: File used to load/save the key/event mappings from. Resetmapper only works with the defaul value. -# usescancodes: Avoid usage of symkeys, might not work on all operating systems. - -fullscreen=TRUE -fulldouble=false -fullresolution=Fixed -windowresolution=1280x800 -output=direct3d -autolock=true -sensitivity=100 -waitonerror=true -priority=higher,normal -mapperfile=mapper-0.74.map -usescancodes=true - -[dosbox] -# language: Select another language file. -# machine: The type of machine tries to emulate. -# Possible values: hercules, cga, tandy, pcjr, ega, vgaonly, svga_s3, svga_et3000, svga_et4000, svga_paradise, vesa_nolfb, vesa_oldvbe. -# captures: Directory where things like wave, midi, screenshot get captured. -# memsize: Amount of memory DOSBox has in megabytes. -# This value is best left at its default to avoid problems with some games, -# though few games might require a higher value. -# There is generally no speed advantage when raising this value. - -language= -machine=svga_s3 -captures=.\Captures\ -memsize=16 - -[render] -# frameskip: How many frames DOSBox skips before drawing one. -# aspect: Do aspect correction, if your output method doesn't support scaling this can slow things down!. -# scaler: Scaler used to enlarge/enhance low resolution modes. -# If 'forced' is appended, then the scaler will be used even if the result might not be desired. -# Possible values: none, normal2x, normal3x, advmame2x, advmame3x, advinterp2x, advinterp3x, hq2x, hq3x, 2xsai, super2xsai, supereagle, tv2x, tv3x, rgb2x, rgb3x, scan2x, scan3x. - -frameskip=0 -aspect=false -scaler=normal3x - -[cpu] -# core: CPU Core used in emulation. auto will switch to dynamic if available and appropriate. -# Possible values: auto, dynamic, normal, simple. -# cputype: CPU Type used in emulation. auto is the fastest choice. -# Possible values: auto, 386, 386_slow, 486_slow, pentium_slow, 386_prefetch. -# cycles: Amount of instructions DOSBox tries to emulate each millisecond. -# Setting this value too high results in sound dropouts and lags. -# Cycles can be set in 3 ways: -# 'auto' tries to guess what a game needs. -# It usually works, but can fail for certain games. -# 'fixed #number' will set a fixed amount of cycles. This is what you usually need if 'auto' fails. -# (Example: fixed 4000). -# 'max' will allocate as much cycles as your computer is able to handle. -# -# Possible values: auto, fixed, max. -# cycleup: Amount of cycles to decrease/increase with keycombo.(CTRL-F11/CTRL-F12) -# cycledown: Setting it lower than 100 will be a percentage. - -core=auto -cputype=auto -cycles=max -cycleup=10 -cycledown=20 - -[mixer] -# nosound: Enable silent mode, sound is still emulated though. -# rate: Mixer sample rate, setting any device's rate higher than this will probably lower their sound quality. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# blocksize: Mixer block size, larger blocks might help sound stuttering but sound will also be more lagged. -# Possible values: 1024, 2048, 4096, 8192, 512, 256. -# prebuffer: How many milliseconds of data to keep on top of the blocksize. - -nosound=false -rate=22050 -blocksize=2048 -prebuffer=10 - -[midi] -# mpu401: Type of MPU-401 to emulate. -# Possible values: intelligent, uart, none. -# mididevice: Device that will receive the MIDI data from MPU-401. -# Possible values: default, win32, alsa, oss, coreaudio, coremidi, none. -# midiconfig: Special configuration options for the device driver. This is usually the id of the device you want to use. -# See the README/Manual for more details. - -mpu401=intelligent -mididevice=default -midiconfig= - -[sblaster] -# sbtype: Type of Soundblaster to emulate. gb is Gameblaster. -# Possible values: sb1, sb2, sbpro1, sbpro2, sb16, gb, none. -# sbbase: The IO address of the soundblaster. -# Possible values: 220, 240, 260, 280, 2a0, 2c0, 2e0, 300. -# irq: The IRQ number of the soundblaster. -# Possible values: 7, 5, 3, 9, 10, 11, 12. -# dma: The DMA number of the soundblaster. -# Possible values: 1, 5, 0, 3, 6, 7. -# hdma: The High DMA number of the soundblaster. -# Possible values: 1, 5, 0, 3, 6, 7. -# sbmixer: Allow the soundblaster mixer to modify the DOSBox mixer. -# oplmode: Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'. -# Possible values: auto, cms, opl2, dualopl2, opl3, none. -# oplemu: Provider for the OPL emulation. compat might provide better quality (see oplrate as well). -# Possible values: default, compat, fast. -# oplrate: Sample rate of OPL music emulation. Use 49716 for highest quality (set the mixer rate accordingly). -# Possible values: 44100, 49716, 48000, 32000, 22050, 16000, 11025, 8000. - -sbtype=sb16 -sbbase=220 -irq=7 -dma=1 -hdma=5 -sbmixer=true -oplmode=auto -oplemu=default -oplrate=44100 - -[gus] -# gus: Enable the Gravis Ultrasound emulation. -# gusrate: Sample rate of Ultrasound emulation. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# gusbase: The IO base address of the Gravis Ultrasound. -# Possible values: 240, 220, 260, 280, 2a0, 2c0, 2e0, 300. -# gusirq: The IRQ number of the Gravis Ultrasound. -# Possible values: 5, 3, 7, 9, 10, 11, 12. -# gusdma: The DMA channel of the Gravis Ultrasound. -# Possible values: 3, 0, 1, 5, 6, 7. -# ultradir: Path to Ultrasound directory. In this directory -# there should be a MIDI directory that contains -# the patch files for GUS playback. Patch sets used -# with Timidity should work fine. - -gus=false -gusrate=44100 -gusbase=240 -gusirq=5 -gusdma=3 -ultradir=C:\ULTRASND - -[speaker] -# pcspeaker: Enable PC-Speaker emulation. -# pcrate: Sample rate of the PC-Speaker sound generation. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# tandy: Enable Tandy Sound System emulation. For 'auto', emulation is present only if machine is set to 'tandy'. -# Possible values: auto, on, off. -# tandyrate: Sample rate of the Tandy 3-Voice generation. -# Possible values: 44100, 48000, 32000, 22050, 16000, 11025, 8000, 49716. -# disney: Enable Disney Sound Source emulation. (Covox Voice Master and Speech Thing compatible). - -pcspeaker=true -pcrate=44100 -tandy=auto -tandyrate=44100 -disney=true - -[joystick] -# joysticktype: Type of joystick to emulate: auto (default), none, -# 2axis (supports two joysticks), -# 4axis (supports one joystick, first joystick used), -# 4axis_2 (supports one joystick, second joystick used), -# fcs (Thrustmaster), ch (CH Flightstick). -# none disables joystick emulation. -# auto chooses emulation depending on real joystick(s). -# (Remember to reset dosbox's mapperfile if you saved it earlier) -# Possible values: auto, 2axis, 4axis, 4axis_2, fcs, ch, none. -# timed: enable timed intervals for axis. Experiment with this option, if your joystick drifts (away). -# autofire: continuously fires as long as you keep the button pressed. -# swap34: swap the 3rd and the 4th axis. can be useful for certain joysticks. -# buttonwrap: enable button wrapping at the number of emulated buttons. - -joysticktype=fcs -timed=true -autofire=false -swap34=false -buttonwrap=false - -[serial] -# serial1: set type of device connected to com port. -# Can be disabled, dummy, modem, nullmodem, directserial. -# Additional parameters must be in the same line in the form of -# parameter:value. Parameter for all types is irq (optional). -# for directserial: realport (required), rxdelay (optional). -# (realport:COM1 realport:ttyS0). -# for modem: listenport (optional). -# for nullmodem: server, rxdelay, txdelay, telnet, usedtr, -# transparent, port, inhsocket (all optional). -# Example: serial1=modem listenport:5000 -# Possible values: dummy, disabled, modem, nullmodem, directserial. -# serial2: see serial1 -# Possible values: dummy, disabled, modem, nullmodem, directserial. -# serial3: see serial1 -# Possible values: dummy, disabled, modem, nullmodem, directserial. -# serial4: see serial1 -# Possible values: dummy, disabled, modem, nullmodem, directserial. - -serial1=dummy -serial2=dummy -serial3=disabled -serial4=disabled - -[dos] -# xms: Enable XMS support. -# ems: Enable EMS support. -# umb: Enable UMB support. -# keyboardlayout: Language code of the keyboard layout (or none). - -xms=true -ems=true -umb=true -keyboardlayout=auto - -[ipx] -# ipx: Enable ipx over UDP/IP emulation. - -ipx=false - -[autoexec] -# Lines in this section will be run at startup. -# You can put your MOUNT lines here. - -@echo off -Mount C ".." -C: -cd CD -imgmount d DUNGEO~8.CUE -t iso -fs iso -cd .. -cls -KEEPER.exe -:exit -exit -``` - -
- -- Notice the different in the automount commands - - Mount the games files to can - - Change to C: - - Change Directory to CD - - Using dosbox imgmount, mount the CD to the D path - - cd to the directory above where the .exe - - Clear the working out - - Run KEEPER.exe -- Once this is done, the game will run. Zip the files up and add to your RomM and give it a test. - -#### Advanced Game Troubleshooting - -If the games are not working and you go back to a blank dos looking screen, this will require some manual troubleshooting but I have found success with the following methods: - -- Remove the .exe in the .CONF file and investigate the automounts - - This means going to the C: and make sure the .exe is in the right place. - - Use the following command `type DEFAULT.cfg` to see the installation location, this is where the game is looking for the files, this should be set to a D: location. - - Browse to that location and see if the files are there and they are readable. -- The automount is simply following instructions, so it expects the files to be where you say, following this method is usually best to troubleshoot. - -Another way of troubleshooting is to use Retroarch and the core "dosbox-pure" because if it runs in here, it will run in RomM. Simply give it your ZIP and it will act the same as if you was doing it through RomM, once the .conf file is perfected add it back to the ZIP. diff --git a/docs/Platforms-and-Players/RuffleRS-Player.md b/docs/Platforms-and-Players/RuffleRS-Player.md deleted file mode 100644 index 68089843..00000000 --- a/docs/Platforms-and-Players/RuffleRS-Player.md +++ /dev/null @@ -1,7 +0,0 @@ - - -[Ruffle](https://ruffle.rs/) is a web-based player for flash games. With flash now discontinued, this is the best way to play your flash collection in the browser. - - -!!! important - Ruffle will only play games stored in platform folders called `flash` or `browser`. diff --git a/docs/Platforms-and-Players/Supported-Platforms.md b/docs/Platforms-and-Players/Supported-Platforms.md deleted file mode 100644 index d1a5e8bc..00000000 --- a/docs/Platforms-and-Players/Supported-Platforms.md +++ /dev/null @@ -1,660 +0,0 @@ - - - -Below is a list of all supported platforms/systems/consoles and their respective folder names. Supported platforms means RomM can fetch metadata from sources for those platforms. - - -!!! info - For platforms that can be playable in the browser, please check [EmulatorJS supported platforms](./EmulatorJS-Player.md) and [RuffleRS player](./RuffleRS-Player.md). - - -!!! danger - **The folder name is case-sensitive and must be used exactly as it appears in the list below.** - -
-

Filter providers

-
-
- - - - - - - - -
-
- 0 of 0 platforms shown -
-
-
- - - - - - -|Platform Name|Folder Name|Metadata Providers| -|---|---|---| -| 1292 Advanced Programmable Video System | `1292-advanced-programmable-video-system` | igdb logo mobygames logo | -| 3DO Interactive Multiplayer | `3do` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| 8-Bit Productions Commander X16 | `commander-x16` | hasheous logo | -| Aamber Pegasus | `pegasus` | launchbox logo | -| ABC 80 | `abc-80` | mobygames logo | -| Acorn Archimedes | `acorn-archimedes` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Acorn Electron | `acorn-electron` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Advanced Pico Beena | `advanced-pico-beena` | igdb logo | -| Adventure Vision | `adventure-vision` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| AirConsole | `airconsole` | igdb logo mobygames logo | -| Alice 32/90 | `alice-3290` | mobygames logo launchbox logo | -| Altair 680 | `altair-680` | mobygames logo | -| Altair 8800 | `altair-8800` | mobygames logo hasheous logo | -| Amazon Alexa | `amazon-alexa` | mobygames logo | -| Amazon Fire TV | `amazon-fire-tv` | igdb logo mobygames logo | -| Amiga | `amiga` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Amiga CD | `amiga-cd` | screenscraper logo | -| Amiga CD32 | `amiga-cd32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Amstrad CPC | `acpc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Amstrad GX4000 | `amstrad-gx4000` | igdb logo screenscraper logo launchbox logo hasheous logo | -| Amstrad PCW | `amstrad-pcw` | igdb logo mobygames logo hasheous logo | -| Analogue electronics | `analogueelectronics` | igdb logo | -| Android | `android` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Antstream | `antstream` | mobygames logo | -| APF MP1000/Imagination Machine | `apf` | mobygames logo launchbox logo hasheous logo | -| Apogee BK-01 | `bk-01` | launchbox logo | -| Apple I | `apple` | mobygames logo hasheous logo | -| Apple II | `appleii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Apple IIGS | `apple-iigs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Apple III | `appleiii` | hasheous logo | -| Apple Lisa | `apple-lisa` | hasheous logo | -| Apple Pippin | `apple-pippin` | igdb logo hasheous logo | -| Arcade | `arcade` | igdb logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Arcadia 2001 | `arcadia-2001` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo | -| Arduboy | `arduboy` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | -| Astral 2000 | `astral-2000` | mobygames logo | -| Atari 2600 | `atari2600` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Atari 5200 | `atari5200` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Atari 7800 | `atari7800` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Atari 8-bit | `atari8bit` | igdb logo screenscraper logo mobygames logo hasheous logo howlongtobeat logo | -| Atari 800 | `atari800` | launchbox logo | -| Atari Jaguar | `jaguar` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Atari Jaguar CD | `atari-jaguar-cd` | igdb logo launchbox logo retroachivements logo howlongtobeat logo | -| Atari Lynx | `lynx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Atari ST/STE | `atari-st` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Atari VCS | `atari-vcs` | mobygames logo | -| Atari XEGS | `atari-xegs` | launchbox logo | -| Atom | `atom` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| AY-3-8500 | `ay-3-8500` | igdb logo | -| AY-3-8603 | `ay-3-8603` | igdb logo | -| AY-3-8605 | `ay-3-8605` | igdb logo | -| AY-3-8606 | `ay-3-8606` | igdb logo | -| AY-3-8607 | `ay-3-8607` | igdb logo | -| AY-3-8610 | `ay-3-8610` | igdb logo | -| AY-3-8710 | `ay-3-8710` | igdb logo | -| AY-3-8760 | `ay-3-8760` | igdb logo | -| Bada | `bada` | mobygames logo | -| Bally Astrocade | `astrocade` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| BBC Microcomputer System | `bbcmicro` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Benesse Pocket Challenge V2 | `pocket-challenge-v2` | hasheous logo | -| Benesse Pocket Challenge W | `pocket-challenge-w` | hasheous logo | -| BeOS | `beos` | mobygames logo | -| BGR Computers Excalibur 64 | `excalibur-64` | hasheous logo | -| Bit Corporation BIT 90 | `bit-90` | hasheous logo | -| Black Point | `black-point` | | -| BlackBerry OS | `blackberry` | igdb logo mobygames logo | -| Blacknut | `blacknut` | mobygames logo | -| Blu-ray Player | `blu-ray-player` | igdb logo mobygames logo | -| BREW | `brew` | mobygames logo | -| Browser (Flash/HTML5) | `browser` | igdb logo mobygames logo launchbox logo flashpoint logo howlongtobeat logo | -| Bubble | `bubble` | mobygames logo | -| Call-A-Computer time-shared mainframe computer system | `call-a-computer` | igdb logo | -| Cambridge Computer Z88 | `z88` | hasheous logo | -| Camputers Lynx | `camputers-lynx` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| Casio CFX-9850 | `casio-cfx-9850` | hasheous logo | -| Casio FP-1000 & FP-1100 | `casio-fp-1000` | hasheous logo | -| Casio Loopy | `casio-loopy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Casio PB-1000 | `casio-pb-1000` | hasheous logo | -| Casio Programmable Calculator | `casio-programmable-calculator` | mobygames logo | -| Casio PV-1000 | `casio-pv-1000` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| Casio PV-2000 | `casio-pv-2000` | hasheous logo | -| CDC Cyber 70 | `cdccyber70` | igdb logo | -| Champion 2711 | `champion-2711` | mobygames logo | -| ClickStart | `clickstart` | mobygames logo | -| Coleco Adam | `colecoadam` | screenscraper logo mobygames logo launchbox logo | -| ColecoVision | `colecovision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Colour Genie | `colour-genie` | screenscraper logo mobygames logo launchbox logo | -| Commodore 128 | `c128` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| Commodore 16 | `c16` | igdb logo screenscraper logo mobygames logo hasheous logo | -| Commodore C64/128/MAX | `c64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Commodore CDTV | `commodore-cdtv` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Commodore PET | `cpet` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Commodore Plus/4 | `c-plus-4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Commodore VIC-20 | `vic-20` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Compal 80 | `compal-80` | mobygames logo | -| Compucolor I | `compucolor-i` | mobygames logo | -| Compucolor II | `compucolor-ii` | mobygames logo | -| Compucorp Programmable Calculator | `compucorp-programmable-calculator` | mobygames logo | -| COSMAC | `fred-cosmac` | mobygames logo | -| CP/M | `cpm` | mobygames logo | -| CreatiVision | `creativision` | screenscraper logo mobygames logo launchbox logo | -| Cybervision | `cybervision` | mobygames logo | -| Danger OS | `danger-os` | mobygames logo | -| Daydream | `daydream` | igdb logo | -| DEC GT40 | `gt40` | igdb logo | -| Dedicated console | `dedicated-console` | mobygames logo | -| Dedicated handheld | `dedicated-handheld` | mobygames logo | -| Didj | `didj` | mobygames logo | -| Digiblast | `digiblast` | igdb logo mobygames logo | -| DoJa | `doja` | mobygames logo | -| Donner Model 30 | `donner30` | igdb logo | -| DOS | `dos` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Dragon 32/64 | `dragon-32-slash-64` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Dreamcast | `dc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| DVD Player | `dvd-player` | igdb logo mobygames logo howlongtobeat logo | -| e-Reader / Card-e Reader | `e-reader-slash-card-e-reader` | igdb logo | -| ECD Micromind | `ecd-micromind` | mobygames logo | -| EDSAC | `edsac` | igdb logo | -| Elektor TV Games Computer | `elektor` | igdb logo retroachivements logo | -| Elektronika BK | `bk` | launchbox logo | -| Enterprise | `enterprise` | mobygames logo launchbox logo | -| Epoch Cassette Vision | `epoch-cassette-vision` | igdb logo mobygames logo | -| Epoch Game Pocket Computer | `epoch-game-pocket-computer` | screenscraper logo mobygames logo launchbox logo | -| Epoch Super Cassette Vision | `epoch-super-cassette-vision` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Evercade | `evercade` | igdb logo mobygames logo howlongtobeat logo | -| Exelvision | `exelvision` | screenscraper logo mobygames logo launchbox logo | -| ExEn | `exen` | mobygames logo | -| Exidy Sorcerer | `exidy-sorcerer` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Fairchild Channel F | `fairchild-channel-f` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo | -| Family Computer | `famicom` | igdb logo mobygames logo launchbox logo retroachivements logo | -| Family Computer Disk System | `fds` | igdb logo screenscraper logo launchbox logo hasheous logo | -| Feature phone | `mobile-custom` | mobygames logo | -| Ferranti Nimrod Computer | `nimrod` | igdb logo | -| FM Towns | `fm-towns` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| FM-7 | `fm-7` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| Freebox | `freebox` | mobygames logo | -| G-cluster | `g-cluster` | mobygames logo | -| Galaksija | `galaksija` | mobygames logo | -| Gamate | `gamate` | igdb logo hasheous logo | -| Game & Watch | `g-and-w` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| Game Boy | `gb` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Game Boy Advance | `gba` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Game Boy Color | `gbc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Game Master | `hartung` | screenscraper logo launchbox logo | -| Game Wave | `game-wave` | mobygames logo launchbox logo | -| Game.com | `game-dot-com` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| GameStick | `gamestick` | mobygames logo | -| Gear VR | `gear-vr` | igdb logo howlongtobeat logo | -| GIMINI | `gimini` | mobygames logo | -| Gizmondo | `gizmondo` | igdb logo mobygames logo howlongtobeat logo | -| Gloud | `gloud` | mobygames logo | -| Glulx | `glulx` | mobygames logo | -| GNEX | `gnex` | mobygames logo | -| Google Stadia | `stadia` | igdb logo mobygames logo howlongtobeat logo | -| GP2X | `gp2x` | mobygames logo | -| GP2X Wiz | `gp2x-wiz` | mobygames logo | -| GP32 | `gp32` | screenscraper logo mobygames logo launchbox logo | -| GVM | `gvm` | mobygames logo | -| Handheld Electronic LCD | `handheld-electronic-lcd` | igdb logo | -| HD DVD Player | `hd-dvd-player` | mobygames logo | -| Heath/Zenith H8/H89 | `heathzenith` | mobygames logo | -| Heathkit H11 | `heathkit-h11` | mobygames logo | -| Hector HRX | `hrx` | launchbox logo | -| Hitachi S1 | `hitachi-s1` | mobygames logo | -| HP 2100 | `hp2100` | igdb logo | -| HP 3000 | `hp3000` | igdb logo | -| HP 9800 | `hp-9800` | mobygames logo | -| HP Programmable Calculator | `hp-programmable-calculator` | mobygames logo | -| Hugo | `hugo` | mobygames logo | -| Hyper Neo Geo 64 | `hyper-neo-geo-64` | igdb logo | -| HyperScan | `hyperscan` | igdb logo mobygames logo launchbox logo | -| IBM 5100 | `ibm-5100` | mobygames logo | -| IBM PCjr | `pc-jr` | hasheous logo | -| Ideal-Computer | `ideal-computer` | mobygames logo | -| iiRcade | `iircade` | mobygames logo | -| Imlac PDS-1 | `imlac-pds1` | igdb logo | -| Intel 8008 | `intel-8008` | mobygames logo | -| Intel 8080 | `intel-8080` | mobygames logo | -| Intel 8086 / 8088 | `intel-8086` | mobygames logo | -| Intellivision | `intellivision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Intellivision Amico | `intellivision-amico` | igdb logo | -| Interact Model One | `interact-model-one` | mobygames logo | -| Interton VC 4000 | `interton-vc-4000` | retroachivements logo | -| Interton Video 2000 | `interton-video-2000` | mobygames logo | -| iOS | `ios` | igdb logo mobygames logo launchbox logo | -| iPad | `ipad` | mobygames logo | -| iPod Classic | `ipod-classic` | mobygames logo | -| J2ME | `j2me` | mobygames logo | -| Jolt | `jolt` | mobygames logo | -| Jupiter Ace | `jupiter-ace` | screenscraper logo mobygames logo launchbox logo | -| KaiOS | `kaios` | mobygames logo | -| KIM-1 | `kim-1` | mobygames logo | -| Kindle Classic | `kindle` | mobygames logo | -| Laser 200 | `laser200` | mobygames logo | -| LaserActive | `laseractive` | igdb logo mobygames logo | -| LeapFrog Explorer | `leapfrog-explorer` | mobygames logo | -| Leapster | `leapster` | igdb logo mobygames logo | -| Leapster Explorer/LeadPad Explorer | `leapster-explorer-slash-leadpad-explorer` | igdb logo mobygames logo | -| LeapTV | `leaptv` | igdb logo mobygames logo | -| Legacy Computer | `legacy-computer` | igdb logo | -| Legacy Mobile Device | `mobile` | igdb logo howlongtobeat logo | -| Linux | `linux` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Luna | `luna` | mobygames logo howlongtobeat logo | -| Mac | `mac` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Maemo | `maemo` | mobygames logo | -| Magnavox Odyssey | `odyssey` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Mainframe | `mainframe` | mobygames logo | -| Matsushita/Panasonic JR | `matsushitapanasonic-jr` | mobygames logo | -| Mattel Aquarius | `aquarius` | mobygames logo launchbox logo hasheous logo | -| MeeGo | `meego` | mobygames logo | -| Mega Duck/Cougar Boy | `mega-duck-slash-cougar-boy` | igdb logo launchbox logo retroachivements logo | -| Memotech MTX | `memotech-mtx` | mobygames logo | -| Memotech MTX512 | `mtx512` | launchbox logo | -| Meritum | `meritum` | mobygames logo | -| Meta Quest 2 | `meta-quest-2` | igdb logo | -| Meta Quest 3 | `meta-quest-3` | igdb logo | -| Microbee | `microbee` | mobygames logo hasheous logo | -| Microcomputer | `microcomputer` | igdb logo | -| Microsoft MSX2+ | `msx2plus` | launchbox logo | -| Microtan 65 | `microtan-65` | mobygames logo | -| Microvision | `microvision` | igdb logo mobygames logo | -| Mophun | `mophun` | mobygames logo | -| MOS Technology 6502 | `mos-technology-6502` | mobygames logo | -| Motorola 6800 | `motorola-6800` | mobygames logo | -| Motorola 68k | `motorola-68k` | mobygames logo | -| MRE | `mre` | mobygames logo | -| MSX | `msx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| MSX Turbo R | `msx-turbo` | screenscraper logo | -| MSX2 | `msx2` | igdb logo launchbox logo hasheous logo | -| MUGEN | `mugen` | launchbox logo | -| N-Gage | `ngage` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| N-Gage (service) | `ngage2` | mobygames logo | -| Namco System 22 | `system-32` | launchbox logo | -| Nascom | `nascom` | mobygames logo | -| NEC PC-6000 Series | `nec-pc-6000-series` | igdb logo hasheous logo | -| Neo Geo AES | `neogeoaes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Neo Geo CD | `neo-geo-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Neo Geo MVS | `neogeomvs` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Neo Geo Pocket | `neo-geo-pocket` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Neo Geo Pocket Color | `neo-geo-pocket-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo | -| Neo Geo X | `neo-geo-x` | mobygames logo | -| New Nintendo 3DS | `new-nintendo-3ds` | igdb logo mobygames logo hasheous logo | -| NewBrain | `newbrain` | mobygames logo | -| Newton | `newton` | mobygames logo | -| Nintendo 3DS | `3ds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Nintendo 64 | `n64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Nintendo 64DD | `64dd` | igdb logo screenscraper logo launchbox logo hasheous logo | -| Nintendo DS | `nds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Nintendo DSi | `nintendo-dsi` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | -| Nintendo Entertainment System | `nes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Nintendo GameCube | `ngc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Nintendo Switch | `switch` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Nintendo Switch 2 | `switch-2` | igdb logo mobygames logo launchbox logo howlongtobeat logo | -| North Star | `northstar` | mobygames logo | -| Noval 760 | `noval-760` | mobygames logo | -| Nuon | `nuon` | igdb logo mobygames logo launchbox logo | -| Oculus Go | `oculus-go` | igdb logo mobygames logo howlongtobeat logo | -| Oculus Quest | `oculus-quest` | igdb logo mobygames logo howlongtobeat logo | -| Oculus Rift | `oculus-rift` | igdb logo | -| Oculus VR | `oculus-vr` | igdb logo | -| Odyssey 2 | `odyssey-2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Ohio Scientific | `ohio-scientific` | mobygames logo | -| OnLive Game System | `onlive-game-system` | igdb logo mobygames logo howlongtobeat logo | -| OOParts | `ooparts` | igdb logo mobygames logo | -| OpenBOR | `openbor` | launchbox logo | -| Orao | `orao` | mobygames logo | -| Oric | `oric` | screenscraper logo mobygames logo | -| Oric Atmos | `atmos` | launchbox logo | -| OS/2 | `os2` | mobygames logo | -| Othello Multivision | `multivision` | launchbox logo hasheous logo | -| Ouya | `ouya` | igdb logo mobygames logo launchbox logo howlongtobeat logo | -| Palm OS | `palm-os` | igdb logo screenscraper logo mobygames logo | -| Palmtex | `palmtex` | | -| Panasonic Jungle | `panasonic-jungle` | igdb logo | -| Panasonic M2 | `panasonic-m2` | igdb logo | -| Pandora | `pandora` | mobygames logo | -| PC Booter | `pc-booter` | mobygames logo | -| PC Engine SuperGrafx | `supergrafx` | igdb logo screenscraper logo mobygames logo launchbox logo | -| PC-50X Family | `pc-50x-family` | igdb logo | -| PC-6001 | `pc-6001` | mobygames logo | -| PC-8000 | `pc-8000` | mobygames logo | -| PC-8800 Series | `pc-8800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| PC-9800 Series | `pc-9800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PC-FX | `pc-fx` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo howlongtobeat logo | -| PDP-1 | `pdp1` | igdb logo | -| PDP-10 | `pdp10` | igdb logo | -| PDP-11 | `pdp11` | igdb logo | -| PDP-7 | `pdp-7` | igdb logo | -| PDP-8 | `pdp-8` | igdb logo | -| Pebble | `pebble` | mobygames logo | -| Philips CD-i | `philips-cd-i` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Philips VG 5000 | `philips-vg-5000` | screenscraper logo mobygames logo launchbox logo | -| Photo CD | `photocd` | mobygames logo | -| PICO | `pico` | screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| Pinball | `pinball` | launchbox logo | -| Pippin | `pippin` | mobygames logo | -| PLATO | `plato` | igdb logo | -| Playdate | `playdate` | igdb logo mobygames logo howlongtobeat logo | -| Playdia | `playdia` | igdb logo mobygames logo | -| PlayStation | `psx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| PlayStation 2 | `ps2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| PlayStation 3 | `ps3` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation 4 | `ps4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation 5 | `ps5` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation Now | `playstation-now` | mobygames logo howlongtobeat logo | -| PlayStation Portable | `psp` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| PlayStation Vita | `psvita` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation VR | `psvr` | igdb logo howlongtobeat logo | -| PlayStation VR2 | `psvr2` | igdb logo | -| Plex Arcade | `plex-arcade` | mobygames logo | -| Plug & Play | `plug-and-play` | igdb logo howlongtobeat logo | -| PocketStation | `pocketstation` | igdb logo launchbox logo hasheous logo | -| Pokitto | `pokitto` | mobygames logo | -| Pokémon mini | `pokemon-mini` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo | -| Poly-88 | `poly-88` | mobygames logo | -| Polymega | `polymega` | igdb logo | -| R-Zone | `r-zone` | igdb logo | -| RCA Studio II | `rca-studio-ii` | mobygames logo launchbox logo hasheous logo | -| Research Machines 380Z | `research-machines-380z` | mobygames logo | -| Roku | `roku` | mobygames logo | -| SAM Coupé | `sam-coupe` | screenscraper logo mobygames logo launchbox logo | -| Satellaview | `satellaview` | igdb logo launchbox logo | -| SC/MP | `scmp` | mobygames logo | -| ScummVM | `scummvm` | launchbox logo | -| SD-200/270/290 | `sd-200270290` | mobygames logo | -| SDS Sigma 7 | `sdssigma7` | igdb logo | -| Sega 32X | `sega32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Sega Advanced Pico Beena | `beena` | hasheous logo | -| Sega CD | `segacd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Sega CD 32X | `segacd32` | igdb logo launchbox logo | -| Sega Dreamcast VMU | `vmu` | launchbox logo | -| Sega Game Gear | `gamegear` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Sega Hikaru | `hikaru` | launchbox logo | -| Sega Master System/Mark III | `sms` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Sega Mega Drive/Genesis | `genesis` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Sega Model 1 | `model1` | launchbox logo | -| Sega Model 2 | `model2` | launchbox logo | -| Sega Model 3 | `model3` | launchbox logo | -| Sega Pico | `sega-pico` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Sega Saturn | `saturn` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Sega SC-3000 | `sc3000` | launchbox logo hasheous logo | -| Sega ST-V | `stv` | launchbox logo | -| Sega System 16 | `system16` | launchbox logo | -| Sega System 32 | `system32` | launchbox logo | -| SG-1000 | `sg1000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Sharp MZ-2200 | `sharp-mz-2200` | igdb logo | -| Sharp MZ-80B/2000/2500 | `sharp-mz-80b20002500` | mobygames logo launchbox logo | -| Sharp MZ-80K/700/800/1500 | `sharp-mz-80k7008001500` | mobygames logo | -| Sharp X1 | `x1` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Sharp X68000 | `sharp-x68000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Sharp Zaurus | `sharp-zaurus` | mobygames logo | -| Signetics 2650 | `signetics-2650` | mobygames logo | -| Sinclair QL | `sinclair-ql` | igdb logo mobygames logo hasheous logo | -| Sinclair ZX81 | `zx81` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| SK-VM | `sk-vm` | mobygames logo | -| SMC-777 | `smc-777` | mobygames logo | -| Socrates | `socrates` | mobygames logo launchbox logo | -| Sol-20 | `sol-20` | igdb logo mobygames logo | -| Sony PSP Minis | `psp-minis` | launchbox logo | -| Sord M5 | `sord-m5` | mobygames logo launchbox logo | -| Spectravideo | `spectravideo` | screenscraper logo mobygames logo launchbox logo | -| SRI-500/1000 | `sri-5001000` | mobygames logo | -| SteamVR | `steam-vr` | igdb logo | -| Sufami Turbo | `sufami-turbo` | screenscraper logo | -| Super A'Can | `super-acan` | igdb logo screenscraper logo mobygames logo | -| Super Famicom | `sfam` | igdb logo mobygames logo launchbox logo hasheous logo retroachivements logo | -| Super NES CD-ROM System | `super-nes-cd-rom-system` | igdb logo | -| Super Nintendo Entertainment System | `snes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Super Vision 8000 | `super-vision-8000` | mobygames logo launchbox logo hasheous logo | -| Sure Shot HD | `sure-shot-hd` | mobygames logo | -| SwanCrystal | `swancrystal` | igdb logo | -| SWTPC 6800 | `swtpc-6800` | mobygames logo | -| Symbian | `symbian` | mobygames logo | -| TADS | `tads` | mobygames logo | -| Taito Type X | `type-x` | launchbox logo | -| Taito X-55 | `taito-x-55` | screenscraper logo mobygames logo | -| Tandy Vis | `tandy-vis` | | -| Tapwave Zodiac | `zod` | igdb logo launchbox logo | -| Tatung Einstein | `tatung-einstein` | igdb logo mobygames logo | -| Tektronix 4050 | `tektronix-4050` | mobygames logo | -| Tele-Spiel ES-2201 | `tele-spiel` | mobygames logo | -| Telstar Arcade | `telstar-arcade` | mobygames logo | -| Terebikko / See 'n Say Video Phone | `terebikko-slash-see-n-say-video-phone` | igdb logo | -| Terminal | `terminal` | mobygames logo | -| Texas Instruments TI-82 | `ti-82` | hasheous logo | -| Texas Instruments TI-83 | `ti-83` | hasheous logo | -| Texas Instruments TI-99 | `ti-99` | igdb logo screenscraper logo mobygames logo | -| Thomson MO5 | `thomson-mo5` | igdb logo screenscraper logo mobygames logo | -| Thomson TO | `thomson-to` | screenscraper logo mobygames logo | -| TI Programmable Calculator | `ti-programmable-calculator` | mobygames logo | -| TI-99/4A | `ti-994a` | mobygames logo launchbox logo | -| Tiki 100 | `tiki-100` | mobygames logo | -| TIM | `tim` | mobygames logo | -| Timex Sinclair 2068 | `timex-sinclair-2068` | mobygames logo | -| Tizen | `tizen` | mobygames logo | -| Tomahawk F1 | `tomahawk-f1` | mobygames logo | -| Tomy Tutor | `tomy-tutor` | mobygames logo launchbox logo | -| Tomy Tutor / Pyuta / Grandstand Tutor | `tomy-tutor-slash-pyuta-slash-grandstand-tutor` | igdb logo | -| Triton | `triton` | mobygames logo | -| TRS-80 | `trs-80` | igdb logo mobygames logo launchbox logo hasheous logo | -| TRS-80 Color Computer | `trs-80-color-computer` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| TRS-80 MC-10 | `trs-80-mc-10` | mobygames logo | -| TRS-80 Model 100 | `trs-80-model-100` | mobygames logo | -| TurboGrafx-16/PC Engine | `tg16` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Turbografx-16/PC Engine CD | `turbografx-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| tvOS | `tvos` | mobygames logo | -| Uzebox | `uzebox` | igdb logo screenscraper logo retroachivements logo | -| V.Flash | `vflash` | mobygames logo | -| V.Smile | `vsmile` | igdb logo screenscraper logo mobygames logo launchbox logo | -| VC 4000 | `vc-4000` | igdb logo launchbox logo | -| Vector-06C | `06c` | launchbox logo | -| Vectrex | `vectrex` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Versatile | `versatile` | mobygames logo | -| VideoBrain | `videobrain` | mobygames logo | -| Videopac+ G7400 | `videopac-g7400` | screenscraper logo mobygames logo launchbox logo | -| Virtual Boy | `virtualboy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Virtual Console | `vc` | igdb logo | -| VIS | `vis` | mobygames logo | -| visionOS | `visionos` | igdb logo | -| Visual Memory Unit / Visual Memory System | `visual-memory-unit-slash-visual-memory-system` | igdb logo | -| Wang 2200 | `wang2200` | mobygames logo | -| WASM-4 | `wasm-4` | retroachivements logo | -| Watara/QuickShot Supervision | `supervision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo | -| watchOS | `watchos` | mobygames logo | -| webOS | `webos` | mobygames logo | -| Wii | `wii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Wii U | `wiiu` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Windows | `win` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Windows 3.x | `win3x` | screenscraper logo mobygames logo launchbox logo | -| Windows Apps | `windows-apps` | mobygames logo | -| Windows Mixed Reality | `windows-mixed-reality` | igdb logo | -| Windows Mobile | `windows-mobile` | igdb logo mobygames logo | -| Windows Phone | `winphone` | igdb logo mobygames logo howlongtobeat logo | -| WIPI | `wipi` | mobygames logo | -| WonderSwan | `wonderswan` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| WonderSwan Color | `wonderswan-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo | -| WoW Action Max | `action-max` | launchbox logo hasheous logo | -| XaviXPORT | `xavixport` | mobygames logo launchbox logo | -| Xbox | `xbox` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Xbox 360 | `xbox360` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Xbox Cloud Gaming | `xboxcloudgaming` | mobygames logo | -| Xbox One | `xboxone` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Xbox Series X/S | `series-x-s` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Xerox Alto | `xerox-alto` | mobygames logo | -| Z-machine | `z-machine` | screenscraper logo mobygames logo | -| Zeebo | `zeebo` | igdb logo mobygames logo howlongtobeat logo | -| Zilog Z80 | `z80` | mobygames logo | -| Zilog Z8000 | `zilog-z8000` | mobygames logo | -| ZiNc | `zinc` | launchbox logo | -| Zodiac | `zodiac` | mobygames logo | -| Zune | `zune` | mobygames logo | -| ZX Spectrum | `zxs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| ZX Spectrum Next | `zx-spectrum-next` | mobygames logo | -| ZX80 | `zx80` | mobygames logo hasheous logo | diff --git a/docs/System-Setup/Synology-Setup-Guide.md b/docs/System-Setup/Synology-Setup-Guide.md deleted file mode 100644 index 8ac0c6a3..00000000 --- a/docs/System-Setup/Synology-Setup-Guide.md +++ /dev/null @@ -1,119 +0,0 @@ - - -## External Written Guides - -While you can follow the below guide, [Marius Bogdan Lixandru](https://mariushosting.com/) has written excellent guides which focus on Synology infrastructure and with support for both MariaDB and PostGresSQL: - -- [How to Install RomM on Your Synology NAS (MariaDB)](https://mariushosting.com/how-to-install-romm-on-your-synology-nas/) -- [How to Install RomM With PostgreSQL on Your Synology NAS](https://mariushosting.com/how-to-install-romm-with-postgresql-on-your-synology-nas/) - -We suggest following the above guides if they fit your setup, and the guide below is available for all other use cases. - -## Prerequisites - -This guide assumes you're familiar with Docker and have basic knowledge of server management. You'll need: - -- A Synology NAS or similar server -- Docker installed -- Basic command line knowledge -- Access to manage network settings - -## Setup Process - -### 1. Folder Structure Setup - -#### ROM Storage Folders - -Create the following directory structure for game assets and configuration: - -```bash -mkdir -p /volume1/data/media/games/assets -mkdir -p /volume1/data/media/games/config -``` - -#### ROM Library Structure - -RomM requires a **very specific folder structure** for rom files: - -```bash -mkdir -p /volume1/data/media/games/library/roms -mkdir -p /volume1/data/media/games/library/bios -``` - -Note: For supported platforms and their specific folder names, refer to the [official RomM docs](../Platforms-and-Players/Supported-Platforms.md). - -#### Docker Data Folders - -Create these folders for project and container data: - -```bash -mkdir -p /volume1/docker/romm-project/ -mkdir -p /volume1/docker/romm/resources -mkdir -p /volume1/docker/romm/redis-data -mkdir -p /volume1/docker/mariadb-romm -``` - -### 2. Network Bridge Setup - -Create a new network bridge named `rommbridge` following standard Docker networking practices. You can use [this guide](https://drfrankenstein.co.uk/step-3-setting-up-a-docker-bridge-network-in-container-manager/) for reference. - -### 3. Key Generation - -#### Authentication Key - -Generate your authentication key using: - -```bash -openssl rand -hex 32 -> 03a054b6ca27e0107c5eed552ea66bacd9f3a2a8a91e7595cd462a593f9ecd09 -``` - -Save the output - you'll need it for the `ROMM_AUTH_SECRET_KEY` in your configuration. - -#### API Integration Setup - -Follow the dedicated docs page for [API key generation](../Getting-Started/Metadata-Providers.md) to set up your API keys. - -### 4. MariaDB Configuration - - -!!! important - - This guide uses a dedicated MariaDB container for RomM, but you can use an existing MariaDB instance if preferred - - We're using MariaDB version 10.7 for compatibility - - The container uses port 3306 internally, mapped to 3309 externally - - A simplified health check is implemented for stability - -### 5. Docker Compose Configuration - -Create a `docker-compose.yml` file with the following content: - - -???+ example "Example Docker Compose" - ``` yaml - --8<-- "synology.docker-compose.yml" - ``` - -### 6. Initial Launch - -1. Start the containers using Docker Compose -2. **Be patient!** The container can take a few minutes to setup on first launch -3. Monitor progress through container logs -4. Access RomM through your browser at `http://your-server-ip:7676` - - -!!! important - - Replace placeholder values (UIDs, GIDs, passwords, API keys) with your own - - Ensure proper permissions on all created directories - - Back up your configuration after successful setup - - Monitor logs during initial startup for any errors - -## Troubleshooting - -- If the web interface shows "page not found," wait for initial setup to complete -- For database connection issues, verify MariaDB container health status -- Check logs for both containers if experiencing issues -- Ensure all volumes are properly mounted with correct permissions - -## Contributing - -This guide is an abridged version of ChopFoo's original guide. If you have any suggestions or improvements, please submit a pull request to the [RomM docs](https://github.com/rommapp/docs). diff --git a/docs/System-Setup/TrueNAS-Setup-Guide.md b/docs/System-Setup/TrueNAS-Setup-Guide.md deleted file mode 100644 index 41d5211e..00000000 --- a/docs/System-Setup/TrueNAS-Setup-Guide.md +++ /dev/null @@ -1,74 +0,0 @@ - - -## Prerequisites - -This guide assumes you're familiar with Docker and have basic knowledge of TrueNAS. You'll need: - -- A running TrueNAS installation -- Your games setup in the [required folder structure](https://github.com/rommapp/romm/blob/master/README.md) - -## Setup Process - -### Install through the TrueNAS App Catalog (Recommended) - -#### Step 1: Navigate to RomM app - -Navigate to the App Catalog via Apps (Left navigation bar) -> Discover Apps -> RomM -> Install - -![RomM app](../resources/truenas/appstore.png) - -#### Step 2: Installation configuration - -Step through the installation UI. You will need to supply various credentials per the [Quick Start Guide](../Getting-Started/Quick-Start-Guide.md). Most of the default values will work. - -Note: You will likely want to set certain Storage Configurations to a Dataset within TrueNAS, such as your RomM Library and Assets storage. If you do this, ensure you provide ACL access to the UserID specified above (default: 568, apps user). - -![RomM Library Example](../resources/truenas/app-config.png) - -#### Step 3: Save your configuration - -Save, and you're done! If the app will not boot, refer to [Troubleshooting](#troubleshooting) or head on over to the [Discord](https://discord.gg/P5HtHnhUDH). - -### Install via YAML - -This installation path should only be used in the event that there is a bug with installing through the App Catalog, or you wish to have more flexibility than is provided by the installation UI. - -#### Step 1: Navigate to YAML install - -Navigate to the `Install via YAML` page via Apps (Left navigation bar) -> Discover Apps -> Install via YAML - -![Install via YAML](../resources/truenas/install-via-yaml.png) - -#### Step 2: Paste in the following YML - -Replace any empty values with credentials you've created per the [Quick Start Guide](../Getting-Started/Quick-Start-Guide.md). - - -???+ example "Example Docker Compose" - ``` yaml - --8<-- "truenas.docker-compose.yml" - ``` - -#### Step 3: Save the configuration - -Save, and you're done! If the app will not boot, refer to [Troubleshooting](#troubleshooting) or head on over to the [Discord](https://discord.gg/P5HtHnhUDH). - -## Troubleshooting - -### General - -- Ensure you have replaced empty values (UIDs, GIDs, passwords, API keys) with your own -- Ensure proper permissions are applied within TrueNAS -- Monitor logs via the app bash terminal during for any errors if the app is encountering issues - -### Specific Issues - -#### Permissions issues inside the docker image - -If you are encountering permissions issues with folders internal to the docker image (not your TrueNAS dataset), consider temporarily setting the user to root (user: 0). If you do this, it is recommended you fix local file permissions via shell and return access back to a non-root user. - -In my particular setup, I had to create a user/group in TrueNAS with uid:gid of 1000:1000 and auxiliary group `apps` due to hard-coded values in the RomM docker image. This resolved outstanding issues I had with my instance of RomM talking to its Redis instance. - -## Contributing - -If you have any suggestions or improvements, please submit a pull request to the [RomM docs](https://github.com/rommapp/docs). diff --git a/docs/System-Setup/Unraid-Compose-Setup.md b/docs/System-Setup/Unraid-Compose-Setup.md deleted file mode 100644 index b7d6dcd1..00000000 --- a/docs/System-Setup/Unraid-Compose-Setup.md +++ /dev/null @@ -1,61 +0,0 @@ - - -## Prerequisites - -Before getting started, install the [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) for Unraid. - -Install [Docker Compose Addon](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/) from the CA (Community Apps Store) - -![Docker Compose Addon](../resources/unraid/docker-compose.png) - -### Docker Tab - -You will now see Compose option under the Docker Containers in your Docker Tab in Unraid - -![Docker Compose Section](../resources/unraid/unraid-start.png) - -## Installation - -Click **Add New Stack** - -- Name it **RomM** and click **OK** - -- Click the **Gear Icon**, edit stack, then edit the compose file - -![Edit Stack](../resources/unraid/edit-stack.png) - -- Add in the example Docker Compose from our [example docker-compose.yml](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml) - -- Add your environment variables, for example: API keys, MariaDB info, and metadata providers - - - You can use secrets and fill in information in a separate `.env` file - -![Edit Compose](../resources/unraid/unraid-compose.png) - -- Click the **Gear Icon**, edit stack, then edit the environment file - - - Fill in any environment variables if you used secrets, etc. - -- Make sure to click **Save Changes** after each edit to the compose and environment files - -![Edit Env](../resources/unraid/romm-env.png) - - -!!! warning - You need to make sure you are following either Folder Structure A or B (Recommend A Type) [RomM Folder Structure](https://docs.romm.app/latest/Getting-Started/Folder-Structure/) - - -!!! warning - It's strongly recommended to backup the `appdata` folder (or mount it in a safe location) before updating, since tearing down the container will wipe the resources (covers, screenshots, etc.) - -## Finish - -- Click Compose Up - -![Compose Up Working](../resources/unraid/docker-compose-loading.png) - -Grab `IP:Port` from romm, and open tab in browser in `http://IP:Port` and it should take you to setup screen - -![Compose Up](../resources/unraid/docker-compose-up.png) - -![Romm Setup](../resources/unraid/unraid-success.png) diff --git a/docs/System-Setup/Unraid-Setup-Guide.md b/docs/System-Setup/Unraid-Setup-Guide.md deleted file mode 100644 index 944d61b9..00000000 --- a/docs/System-Setup/Unraid-Setup-Guide.md +++ /dev/null @@ -1,67 +0,0 @@ - - -## Prerequisites - -Before getting started, install the [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) for Unraid. - -### Docker network - -You'll want to create a custom bridge-type network for both containers to communicate with each other. This will prevent a number of common issues Unraid users tend to come across during setup. This can be done with the following command: `docker network create romm`, and you can verify it worked with `docker network ls`. - -![console output](https://github.com/user-attachments/assets/bac31998-1911-4085-b115-8dd93d519b8b) - -### MariaDB - -MariaDB is required to run RomM, so install it from the plugin registry. Only the [official](https://hub.docker.com/_/mariadb) and [linuxserver](https://github.com/linuxserver/docker-mariadb/pkgs/container/mariadb) versions are supported, but **the official version is preferred**. - -![community apps search results for MariaDB](https://github.com/user-attachments/assets/76f4b6ef-5b63-454f-9357-d2920b9afd0e) - -Now fill in all the environment variables; descriptions of the options and sensible defaults are listed in the [example docker-compose.yml](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml) file. - - -!!! warning - The network type must be set to `Custom: romm` - -![MariaDB environment variables](https://github.com/user-attachments/assets/a11906c5-25b2-46f1-906b-451a9ee39dca) - -## Installation - -From the Unraid dashboard, click `APPS` in the navigation bar. In the search bar, search for `romm`, and install the app listed as "OFFICIAL". This one is maintained by our team and is the most up-to-date. - -![RomM official app](https://github.com/user-attachments/assets/57c4d47a-8604-4e8d-b05a-84dd68dda124) - -## Configuration - -Configure the required environment variables, ports and paths as per the [example docker-compose.yml](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml) file. - - -!!! warning - The network type must also be set to `Custom: romm` - -![RomM docker tab](https://github.com/user-attachments/assets/4c4210c2-ed00-4790-a945-65cbe33620b0) - -Apply the changes, then head to the `DOCKER` tab. You should see both containers in a running state, and can access RomM using the IP:PORT of the container (highlighted below). - -![RomM and MariaDB running](https://github.com/user-attachments/assets/cba26de1-d2c9-4fff-88d8-bc7701f0dd88) - - -!!! warning - It's strongly recommended to backup the `appdata` folder (or mount it in a safe location) before updating, since tearing down the container will wipe the resources (covers, screenshots, etc.) - -## Video tutorial - -[DemonWarriorTech](https://www.youtube.com/@DemonWarriorTech) has published [How to Install RomM on Unraid (Beginner Friendly)](https://www.youtube.com/watch?v=Oo5obHNy2iw) on installing and running RomM on Unraid for Beginners with an in depth instructions and explanation of the software install and how to use it. - -[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/Oo5obHNy2iw/0.jpg)](https://www.youtube.com/watch?v=Oo5obHNy2iw) - -[AlienTech42](https://www.youtube.com/@AlienTech42) has published [a great video](https://www.youtube.com/watch?v=ls5YcsFdwLQ) on installing and running RomM on Unraid. While a bit out of date vis-a-vis install instructions, it's still very useful for general setup and debugging. Check it out! - -[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/ls5YcsFdwLQ/0.jpg)](https://www.youtube.com/watch?v=ls5YcsFdwLQ) - -## Unraid community support - -You can find a [support thread](https://forums.unraid.net/topic/149738-support-eurotimmy-romm-rom-manager-by-zurdi15/) in the unraid forums. - -## Shout-outs - -We want to give a special shout-out to @Smurre95 and @sfumat0 for their help documenting this process, and working towards getting RomM listed in CA. 🤝 diff --git a/docs/Tools/Igir-Collection-Manager.md b/docs/Tools/Igir-Collection-Manager.md deleted file mode 100644 index 3af40c25..00000000 --- a/docs/Tools/Igir-Collection-Manager.md +++ /dev/null @@ -1,124 +0,0 @@ - - -[Igir](https://igir.io/) is a zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS. It can be used to rename your ROMs to match the RomM database, and to move them into a new directory structure. - -## Setup - -### Directory structure - -The directory structure is important for running the bulk ROM renaming script. Before running the bulk ROM renaming script, set up your directories as follows: - -```md -. -├── dats/ # DAT files from no-intro.org -├── roms/ # Original ROM collection -├── roms-unverified/ # Working copy of ROMs -└── igir-romm-cleanup.sh -``` - -### Initial Setup Steps - -1. Create a working copy of your ROMs: - - ```bash - cp -r roms/ roms-unverified/ - ``` - - This provides a safe working environment and allows for easy script adjustment if needed. - -2. Download DAT Files: - - - For cartridge-based systems: - - Visit [No-Intro.org Daily Download](https://datomatic.no-intro.org/index.php?page=download&op=daily) - - Download the latest DAT compilation - - For optical media (e.g., PlayStation): - - Visit [redump.org](http://redump.org/downloads/) - - Download platform-specific DAT files - - Extract the DAT files to your `dats` directory. You can optionally extract a subset of the .dat files into the directory instead. - -## Configuration - -Create the cleanup script `igir-romm-cleanup.sh` with the contents below: - -```bash -#!/usr/bin/env bash -set -ou pipefail -cd "$(dirname "${0}")" - -INPUT_DIR=roms-unverified -OUTPUT_DIR=roms-verified - -# Documentation: https://igir.io/ -# Uses dat files: https://datomatic.no-intro.org/index.php?page=download&op=daily -time npx -y igir@latest \ - move \ - extract \ - report \ - test \ - -d dats/ \ - -i "${INPUT_DIR}/" \ - -o "${OUTPUT_DIR}/{romm}/" \ - --input-checksum-quick false \ - --input-checksum-min CRC32 \ - --input-checksum-max SHA256 \ - --only-retail -``` - -Make the script executable: - -```bash -chmod a+x igir-romm-cleanup.sh -``` - -## Usage - -### Run the script - -Run the script. It will generate a new output directory named `roms-verified`, moving the files from `roms-unverified` if its checksum matches any of the known checksums in the DAT files provided. Any ROMs not identified will remain in the `roms-unverified` directory. - -### Manually move over remaining files - -The script may not identify all of the ROMs in your input directory. You can choose to migrate them over manually: - -```bash -npx -y igir@latest \ - move \ - -i roms-unverified/ \ - -o roms-verified/ \ - --dir-mirror -``` - -This will move your ROMs from the input to the output directory, preserving the subdirectory structure. It also cleans up file extensions in the process. - -### Reorganize multi-disc games - -The Igir script will move games that have multiple discs to separate folders. This can confuse RomM's game detection, and those games need to be reorganized into single folders with many discs. - -To do this enter your platform directory, such as `ps` or `psx` and run the following: - -```bash -ls -d Disc | while read file; do - game=$(echo "${file}" | sed -E 's/ ?(Disc.*//') - mkdir -p "${game}" - mv "${file}" "${game}" - m3u="${game}/${game}.m3u" - touch "${m3u}" - echo "${file}" >> "${m3u}" -done -``` - -This will find any directory with `(Disc` in the name and move the files into a new directory without the `(Disc #)` string. For example: - -Before: - -```bash -Final Fantasy VII (Disc 1) (USA) -Final Fantasy VII (Disc 2) (USA) -``` - -Gets combined to: - -```bash -Final Fantasy VII (USA) -``` diff --git a/docs/Troubleshooting/Authentication-Issues.md b/docs/Troubleshooting/Authentication-Issues.md deleted file mode 100644 index 3ff41883..00000000 --- a/docs/Troubleshooting/Authentication-Issues.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: Troubleshooting Authentication -description: Troubleshooting issues relating to authentication ---- - -### Error: `403 Forbidden` - -When authentication is enabled, most endpoints will return a `403 Forbidden` response if you're not authenticated, or if your sessions is in a broken state. The session key can be reset by [clearing your cookies](https://support.google.com/accounts/answer/32050). - -CSRF protection is also enabled, which helps to mitigates [CSRF attacks](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) (useful if your instance is public). If you encounter a `Forbidden (403) CSRF verification failed` error, simply reloading your browser should force it to fetch a fresh CSRF cookie. - -### Error: `Unable to login: CSRF token verification failed` - -This error is known to happen on Chrome, but could happen in other browsers; manually clear your cookies (specifically one called `csrftoken`) and hard reload your browser window (CMD+SHIFT+R on macOS, CTRL+F5 on Windows). - -### Error: `400 Bad Request` on the Websocket endpoint - -If you're running RomM behind a reverse-proxy (Caddy, Nginx, etc.), ensure that Websockets are supported and enabled. This may vary depending on the reverse proxy solution being used. In the case of Nginx Proxy Manager, enable the "Websockets Support" toggle when editing the proxy host. diff --git a/docs/Troubleshooting/Kubernetes-Issues.md b/docs/Troubleshooting/Kubernetes-Issues.md deleted file mode 100644 index cec85010..00000000 --- a/docs/Troubleshooting/Kubernetes-Issues.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Kubernetes Troubleshooting -description: Troubleshooting Kubernetes issues ---- - -### Error: `invalid host in "tcp://:8080" of the "listen" directive in /etc/nginx/conf.d/default.conf:7` - -By default, Kubernetes will grab information about the service object linked to a pod and inject it as an environment variable into the pod. In RomM, this leads to the pod attempting to bind to the service IP address, leading to the above fatal error. - -To resolve thes error, this default Kubernetes behaviour needs to be disabled by setting the `enableServiceLinks` value in the pod spec to `false`. - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: romm - namespace: romm - ... -spec: - ... - template: - ... - spec: - enableServiceLinks: false - ... -``` diff --git a/docs/Troubleshooting/Miscellaneous-Troubleshooting.md b/docs/Troubleshooting/Miscellaneous-Troubleshooting.md deleted file mode 100644 index 2438d3c6..00000000 --- a/docs/Troubleshooting/Miscellaneous-Troubleshooting.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: Miscellaneous Troubleshooting -description: Troubleshooting miscellaneous issues ---- - -### Restarting the container when using SQLite drops all the data/requires a full re-scan - -Verify that the database is mapped to a persistent storage volume in your docker compose or Unraid template. - -```yaml -"/path/to/database:/romm/database" # [Optional] Only needed if ROMM_DB_DRIVER=sqlite or not set -``` - -### Error: `Could not get twitch auth token: check client_id and client_secret` - -This is likely due to mis-configured environment variables; verify that `CLIENT_ID` and `CLIENT_SECRET` are set correctly, and that both match the values in IGDB. - -### How to view RomM logs to assist with troubleshooting - -Each platform may have different logs, but check the stdout logs for docker. For example when using the recommended docker compose setup `docker logs -f romm` will display a continuous stream of the log. Most lines start with `INFO`, `WARNING`, or `ERROR` diff --git a/docs/Troubleshooting/Scanning-Issues.md b/docs/Troubleshooting/Scanning-Issues.md deleted file mode 100644 index 3e2f5fa2..00000000 --- a/docs/Troubleshooting/Scanning-Issues.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Troubleshooting Scanning -description: Troubleshooting issues relating to library scanning ---- - -### Scan is skipping all platforms/ends instantly - -There are a few common reasons why a scan may end instantly/without scanning platforms - -- Badly mounted library: verify that you mounted your ROMs folder at `/romm/library` -- Incorrect permissions: the app needs to read the files and folders in your library, check their permissions with `ls -lh` -- Invalid folder structure: verify that your folder structure matches the one in the [README](https://github.com/rommapp/romm#-folder-structure) - -### ROMs not found for platform X, check `romm` folder structure - -This is the same issue as the one above, and can be quickly solved by verifying your folder structure. RomM expects a library with a folder named `roms` in it, for example: - -- `/server/media/library:/romm/library` -- `/server/media/games/roms:/romm/library/roms` - -### Scan does not recognize a platform - -When scanning the folders mounted in `/library/roms`, the scanner tries to match the folder name with the platform's slug in IGDB. If you notice that the scanner isn't detecting a platform, verify that the folder name matches the slug in the URL of the [platform in IGDB](https://www.igdb.com/platforms). For example, the Nintendo 64DD has the URL , so the folder should be named `64dd`. - -### Scan times out after ~4 hours - -The background scan task times out after 4 hours, which can happen if you have a very large library. The easiest work around is to keep running scans every 4 hours, **without** checking the "Complete re-scan" option. You can also change the timeout via [environment variable](../Getting-Started/Environment-Variables.md) `SCAN_TIMEOUT`. - -### Scan stops before finishing a platform - -Check the logs for RomM, you should find a line that looks like `ERROR: [RomM][scan_handler][2025-04-12 11:48:55]` that explains why the scanner stopped. This can often happen due to a corrupted file or a file the [python zipfile library](https://docs.python.org/3/library/zipfile.html) cannot handle, such as old DOS zip files with backslashes instead of forward slashes. - -### When scanning a very large library with many platforms it is difficult to keep track of which systems have scanned in - -The easiest method is to check the logs via this command, which will list all the scanned platforms since the RomM container was started `docker logs romm 2>/dev/null|egrep 'scan_handler.*Identified as.*🎮'` - -Here is an example output: - -```text -$ docker logs romm 2>/dev/null|egrep 'scan_handler.*Identified as.*🎮' -INFO: [RomM][scan_handler][2025-04-12 11:37:40] Identified as PlayStation 🎮 -INFO: [RomM][scan_handler][2025-04-12 14:39:32] Identified as DOS 🎮 -INFO: [RomM][scan_handler][2025-04-13 12:50:42] Identified as WonderSwan 🎮 -``` diff --git a/docs/Troubleshooting/Synology-Issues.md b/docs/Troubleshooting/Synology-Issues.md deleted file mode 100644 index ec5ebb17..00000000 --- a/docs/Troubleshooting/Synology-Issues.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Troubleshooting Synology -description: Troubleshooting issues when using Synology products ---- - -### ErrNo 13: Access Denied - -We have noticed recently a spate of access denied on Synology systems via Portainer or even docker manager. The ErrNo13 is directly related to Synology and it is a simple permission issue. To fix it please do the following: - -1. Make sure SSH is enabled on your Synology product. Refer to here if it is not [Enable SSH](https://kb.synology.com/en-uk/DSM/tutorial/How_to_login_to_DSM_with_root_permission_via_SSH_Telnet) -2. Connect to SSH and login as your admin username and password (Same login used to login to DSM web page) -3. Take a note on your user:group you can find this by typing ID when logged into SSH. -4. Type the following commands in the SSH window. - -`sudo chown -R user:group /path/to/library` - -`sudo chmod -R a=,a+rX,u+w,g+w /path/to/library` - -`sudo chown -R user:group /path/to/assets` - -`sudo chmod -R a=,a+rX,u+w,g+w /path/to/assets` - -`sudo chown -R user:group /path/to/config` - -`sudo chmod -R a=,a+rX,u+w,g+w /path/to/config` - -You will find the relevant directories in your compose, this is basically the folders where you store your RomM information and we are just resetting permissions. Restart the containers and you should now have no issues scanning information in! - -Any issues please ask in the Discord. - -Thanks to [Docker IDs - DrFrankenstein](https://drfrankenstein.co.uk/step-2-setting-up-a-restricted-docker-user-and-obtaining-ids/) for the guidance from his blog. diff --git a/docs/Usage/Administration.md b/docs/Usage/Administration.md deleted file mode 100644 index 8a565ab4..00000000 --- a/docs/Usage/Administration.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Administration -description: Navigating and using the Administration Menu ---- - -Clicking on your Profile icon from any screen opens the Administration Drawer. This drawer will look slightly different based on your role. The options available can include **Profile**, **User Interface**, **Library Management**, **Administration**, **Server Stats**, and **About**. - -## Profile - -All users can access this screen. From here you can change your username, password, and email. - -Additionally, you can submit your RetroAchievements username and synchronize your achievements here. - -## User Interface - -From here you can: - -- Change your language -- Adjust the color scheme of RomM -- Control which ribbons are visible on the home screen -- Configure how to group your platforms -- Set what information to display on game cards -- Configure autogenerated collections and their sources - -## Library Management - -From this screen you can: - -- Edit platform bindings and versions on the Config tab -- Manage missing games on the Missing Games tab - -## Administration - -From this page you can manage users and check scheduled and manual tasks. - -## Server Stats - -This area displays the number of platforms, games, saves, states, and screenshots on the server, as well as how much disk space this consumes. Platforms can be filtered here to show how much each platform contributes to total size and game count. - -## About - -This pop-up displays your RomM version with links to the community Discord, GitHub, and documentation. diff --git a/docs/Usage/LibraryManagement.md b/docs/Usage/LibraryManagement.md deleted file mode 100644 index 5486c6e4..00000000 --- a/docs/Usage/LibraryManagement.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -title: Library Management -description: Adding, Removing, and Managing Platforms and Games. ---- - -## The Library View - -The default library view shows a few ribbons: **Recently Added**, **Continue Playing**, **Platforms**, and **Collections**. Games and Platforms are presented as cards that can be interacted with. - -The menu bar is constantly visible and includes tools to **Search**, **List Platforms**, **Manage Collections**, **Scan**, go into **Console Mode**, and **Upload** content as well as access your administration panel. - -In all views, a grid icon is present in the upper right of the screen or container. This can be toggled to collapse or expand the container. - -### Game and Platform cards - -Platform cards can be clicked to go directly to the platform. A game card can be interacted with in several ways: putting your mouse over it allows you to download the game, play the game if the system is supported, or open a context menu giving you the option to manually match game information using a metadata agent, edit existing data, refresh the metadata, and add or remove the game from favourites or collections. - -![game card](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/gameCard.png) -![context menu](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/ContextMenu.png) - -Clicking a platform will take you to the platform view, while clicking a game will take you to the game view. - -### Filters - -Filters are present on nearly every screen in RomM, allowing you to quickly drill down to see only the information relevant to your search. - -If you perform a search first, the listed filters will be taken from the metadata of all present games. For example, if you searched for the word "Fox", and then clicked filters and selected a Language, the list of languages available will be taken from the displayed games. - -#### Toggles - -The filters that can be toggled include: - -- **Show Unmatched** - Show only games that have not yet been matched at all. -- **Show Matched** - Show only games that have been matched. -- **Show Favourites** - Show only games that are Favourites. -- **Show Duplicates** - Show only games that have multiple copies. -- **Show Playables** - Show only games that can be played in a browser. -- **Show Missing** - Shows only games that exist in the database but are otherwise not present. -- **Show Verified** - Shows only games that have matched to Hasheous. -- **Show RetroAchievements** - Shows only games that have support on the RetroAchievements website. - -Next there is a platform dropdown. This allows you to select a platform you want to restrict your view to showing. - -After this you can filter by metadata: Game Genre, Franchise, Collections, Company, Age Rating, Region, and Language. -One additional filter exists after these: Status. This allows you to use the "personal" data tab on any game to track your progress of the game (never played, backlogged, complete, etc). - -## The Menu bar - -The menu bar is designed for quick access to the tools of your RomM server. - -### Search - -Simply typing text into the search bar will bring up game cards for each game that matches the query. To the left of the search bar are two icons: the view filters and the new collection button. - -![search bar](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/SearchBar.png) - -### List Platforms - -The Platforms button opens a drawer listing all of the known platforms in RomM. Clicking on any of these will create a search that is limited to that platform. - -### Collections - -The Collections button opens a drawer listing all of your manually created collections which is specific to each user, as well as your autogenerated collections created by RomM. Clicking a collection will take you to the collection view. - -### Scan - -This is where you'll scan for new platforms and games. `Metadata sources` can be selected to determine which providers will be used to fetch metadata. Use the `Platforms` dropdown to restrict your scan to specific platforms. - -The `Scan options` dropdown allows you to choose a specific type of scan to perform: - -- **New Platforms**: This will only look for platforms that are not already in RomM. -- **Quick Scan**: Scans for games that are not in the library yet (fastest). -- **Unmatched Games**: Attempts to match games that are not matched with the selected metadata sources. - - For example, selecting `IGDB` and `ScreenScraper` will scan games that are **not matched** with IGDB **or** ScreenScraper. -- **Update Metadata**: Updates the metadata for games that have been matched with selected metadata sources using the external ID (e.g. IGDB ID). - - For example, selecting `IGDB` and `ScreenScraper` will update the metadata for games that **are matched** with IGDB **or** ScreenScraper, and will use `igdb_id` and/or `ssfr_id` to refetch the metadata from the respective providers. -- **Recalculate Hashes**: Recalculates hashes for all files in the selected platforms. -- **Total Rescan**: Rescans and rematches all games in the selected platforms (slowest). - - This will wipe all existing metadata matches, including the external IDs, and attempt to match them again, like on a fresh scan. _Saves, states and notes will be preserved._ - -### Console - -This button will take you to a new full screen UI that's especially good for navigation with a controller. - -### Upload - -This button allows you to upload games directly to RomM from the user interface. Simply select a platform and then either click to browse to a ROM or drag and drop it onto the interface. - -### Recently Added - -After a scan is performed, the most recently added games can be found on this ribbon. - -### Continue Playing - -After a game has been played for any amount of time, it's added to this row. The context menu gains a new option for games located on this ribbon: remove from playing. This helps prevent the ribbon from being cluttered by games you wanted to test but do not plan on playing long term. - -### Platforms - -The list of platforms presented here will be matched by RomM by directory structure. See [Supported Platforms page](../Platforms-and-Players/Supported-Platforms.md) for more information about naming your directories. - -### Favourites and Collections - -This ribbon contains all the games you've marked as favourites, and any collections you've created or that have been generated by RomM. - -## Platform View - -The platform view shows all of the games for a specific platform. There is a button to show the platform drawer, the platform firmware, as well as the usual filter button. - -### Platform Drawer - -The platform view drawer has a large icon representing the platform, along with its title. - -Below that is a button to upload ROMs and a button to initiate scans. - -Below that is a line showing how many metadata providers are being used for that platform. - -Next comes all the metadata for the platform itself: the name, folder name, category, generation, and so on. - -Following that are the platform settings, which consists of the style of cover used in the game cards for that platform. - -Finally, in the Danger Zone, is a Delete Platform button. This removes the platform from the database, it does NOT touch the files on your system. If you delete a platform in this way, scanning for new platforms will restore it, and all games will have to have their metadata re-matched. - -![platform drawer](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/PlatformView.png) - -### Firmware - -Next to the platform drawer button is the firmware button. This allows you to review all of the firmware for that system, and upload firmware using the user interface. - -Uploading firmware will open a file browse dialog box. Simply select the BIOS file or files you wish to upload and click "Upload". - -## Collection View - -The Collection View is similar to the Platform View. It has a Platform drawer next to the filters, much like how the Platform View has the Platform Drawer, but lacks the Firmware button. - -The Collection metadata will be shown, which includes a game count, who owns the collection, the privacy of the collection, and the ability to delete the collection. - -## Game View - -The game view is broken down into two distinct parts. - -The first container is the poster, with the usual download, play (if the system allows for playing in a web browser) and context menu as well as a new button: copy download link. - -The second container is the game tabs: Details, Game Data, and Personal tabs. These are containers for information about the game, how RomM sees the save data, and your personal notes. Additional tabs may be visible here depending on your configuration, including the Game Manual, data from How Long to Beat, screenshots, as well as related games. -The Details tab includes metadata for the game. This will show where the data comes from, the platform and year of release, as well as the file information like regions and collections. This is the information that is used for filtering. - -The Game Data tab includes save files and save states, the ability to upload and download them, as well as delete them. This data is personal to the logged in user. - -Finally the Personal tab has check boxes denoting if a game is backlogged, being played, or if it should be hidden. User data such as rating, difficulty, % completed, and the status of the game can also be set here. RetroAchievements can be viewed from this tab for supported matched games. - -If How Long To Beat is enabled as a metadata provider, there will also be a tab here for viewing that data as well. diff --git a/docs/Usage/UserManagement.md b/docs/Usage/UserManagement.md deleted file mode 100644 index 5422e27f..00000000 --- a/docs/Usage/UserManagement.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: User Management -description: Adding, Removing, and Managing your Users. ---- - -The Administration panel contains the tools used to manage users on the server. The first user created will always be given the admin role. - -Users can manage their own game saves, save states, their profile icon, and the "Personal" data tab on games. - -## Adding Users - -Adding a user is as simple as providing a username, password, email address, and selecting a role. Users can be added manually using the "Add" button or by inviting a user via link. When inviting a user, you only need to set a role; they will be prompted to complete the rest of the information. - -### Roles Explained - -**Admin** - The highest authority role. This grants full permission to do everything on the server. - -**Editor** - Can edit platforms and games (adding, removing, or scanning for new content) and make collections public. - -**Viewer** - The most restricted role. Can play games and interact with the Personal data tab. - -## Removing Users - -Removing a user is as simple as clicking the red delete icon and confirming the operation. diff --git a/docs/Getting-Started/folder-structure.md b/docs/getting-started/folder-structure.md similarity index 99% rename from docs/Getting-Started/folder-structure.md rename to docs/getting-started/folder-structure.md index d3e24548..ea5a9ea2 100644 --- a/docs/Getting-Started/folder-structure.md +++ b/docs/getting-started/folder-structure.md @@ -248,4 +248,3 @@ Tags are searchable in the search bar — typing `(USA)` returns every game tagg ## Filename metadata tags RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry — covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). - From 39590c642f295211bdf2f6c196a4b0d36df607bd Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 19:40:11 +0000 Subject: [PATCH 022/121] docs: fill remaining placeholder pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five pages were still flagged `status: placeholder`. All filled now. - install/index.md: Install & Deploy hub — deployment-path table, foundational-decisions list, minimum-reading order, and after-you're-running pointers - reference/environment-variables.md: narrative wrapper around the generator-backed env-vars table — how to set env vars via compose / .env / secrets, essentials table, when-they're-read semantics - reference/scheduled-tasks.md: narrative wrapper around the scheduled-tasks table — cron format recap, disabling patterns, where the table comes from - platforms/supported-platforms.md: narrative wrapper around the generated platform-slug table — column explanations, what-to-do when a platform isn't listed, generator info - developers/api-reference.md: was an empty stub. Now a proper catalogue by endpoint group (auth, ROMs, platforms, collections, assets, firmware, devices+sync, play sessions, tokens, search, tasks, config, stats, feeds, exports, heartbeat, netplay) with pointers to the interactive /api/docs + /api/redoc and Consuming OpenAPI for codegen Builds clean with --strict. --- docs/developers/api-reference.md | 198 +++++++++++++++++++++++- docs/install/index.md | 46 +++++- docs/platforms/supported-platforms.md | 50 +++++- docs/reference/environment-variables.md | 72 ++++++++- docs/reference/scheduled-tasks.md | 51 +++++- 5 files changed, 388 insertions(+), 29 deletions(-) diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md index c6fb9d8e..b8f8ec3c 100644 --- a/docs/developers/api-reference.md +++ b/docs/developers/api-reference.md @@ -1,10 +1,198 @@ --- -status: placeholder -wave: 1 +title: API Reference +description: Catalogue of RomM's REST API — authoritative interactive docs live on each instance. --- # API Reference -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth — this page catalogues what's there, but every running RomM instance serves its own interactive browser with live "try it out" functionality. + +## Interactive docs + +Every RomM instance hosts two renderings of its own spec: + +- **Swagger UI** — `{romm_url}/api/docs` — explore + try endpoints inline. +- **ReDoc** — `{romm_url}/api/redoc` — cleaner reading layout. + +The raw spec: + +```text +{romm_url}/openapi.json +``` + +For code generation, Postman imports, and schema-validation libraries, see [Consuming OpenAPI](openapi.md). + +## Base URL + +```text +{romm_url}/api +``` + +## Auth + +Every write endpoint (and most read endpoints) requires a credential. Five modes supported: + +- **Session cookie** — from the web UI. +- **HTTP Basic** — username + password header. +- **OAuth2 Bearer** — JWT access token. +- **Client API Token** — long-lived `rmm_...` bearer token. +- **OIDC session** — after IdP callback, same as a regular session. + +Full walkthrough in [API Authentication](api-authentication.md). + +## Endpoint groups + +Every endpoint belongs to a group. Summaries below; the interactive docs at `/api/docs` have full request/response schemas. + +### Auth & users + +- `POST /api/auth/login`, `/api/auth/logout` — session login/logout. +- `POST /api/token` — OAuth2 token endpoint (password + refresh grants). +- `GET /api/auth/openid`, `/api/oauth/openid` — OIDC flow. +- `POST /api/auth/forgot-password`, `/api/auth/reset-password` — password reset. +- `GET/POST/PUT/DELETE /api/users` — user management. See [Users & Roles](../administration/users-and-roles.md). +- `POST /api/users/register` — claim an invite link. +- `POST /api/users/invite-link` — generate one. + +### ROMs + +- `GET /api/roms` — list with filters, pagination, search. +- `GET /api/roms/{id}` — single ROM details. +- `POST /api/roms/upload/{start,chunk,complete,cancel}` — chunked upload. +- `PUT /api/roms/{id}` — update metadata. +- `PUT /api/roms/{id}/props` — update per-user data (rating, status). +- `POST /api/roms/delete` — bulk delete. +- `GET /api/roms/{id}/content/{filename}` — download. +- `GET /api/roms/download?ids=...` — bulk zip download. +- `GET /api/roms/by-hash`, `/api/roms/by-metadata-provider` — lookup helpers. + +### ROM notes, files, manuals + +- `GET/POST/PUT/DELETE /api/roms/{id}/notes` — per-ROM notes (optional public). +- `GET /api/roms/{id}/files/content/{filename}` — individual file in a multi-file ROM. +- `POST/DELETE /api/roms/{id}/manuals` — PDF manual management. + +### Platforms + +- `GET /api/platforms` — list. +- `GET /api/platforms/supported` — everything RomM recognises (same data as [Supported Platforms](../platforms/supported-platforms.md)). +- `PUT/DELETE /api/platforms/{id}` — edit or delete. + +### Collections + +- `GET/POST/PUT/DELETE /api/collections` — standard collections. +- `POST/DELETE /api/collections/{id}/roms` — add/remove ROMs. +- `GET/POST/PUT/DELETE /api/collections/smart` — [Smart Collections](../using/smart-collections.md). +- `GET /api/collections/virtual` — read-only [Virtual Collections](../using/virtual-collections.md). + +### Assets — saves, states, screenshots + +- `GET/POST/PUT /api/saves` — save files. See [Saves & States](../using/saves-and-states.md). +- `POST /api/saves/delete` — bulk delete. +- `GET/POST/PUT /api/states` — emulator states. +- `POST /api/screenshots` — screenshot upload. + +### Firmware + +- `GET/POST/DELETE /api/firmware` — firmware management. See [Firmware Management](../administration/firmware-management.md). +- `GET /api/firmware/{id}/content/{filename}` — download. + +### Devices & sync + +- `GET/POST/PUT/DELETE /api/devices` — registered device management. +- `POST /api/sync/negotiate` — sync-session negotiation. +- `POST /api/sync/sessions/{id}/complete` — close a sync session with ingested play sessions. +- `POST /api/devices/{id}/push-pull` — trigger manual sync. + +See [Device Sync Protocol](../ecosystem/device-sync-protocol.md) for the wire-level walkthrough. + +### Play sessions + +- `GET/POST/DELETE /api/play-sessions` — ingest and query play sessions (up to 100 per POST). + +### Client API tokens + +- `GET/POST/DELETE /api/client-tokens` — user's tokens. +- `PUT /api/client-tokens/{id}/regenerate` — regenerate the secret. +- `POST /api/client-tokens/{id}/pair` — generate a pairing code. +- `POST /api/client-tokens/exchange` — exchange a pairing code for a token. +- `GET /api/client-tokens/pair/{code}/status` — poll pairing status. +- `GET /api/client-tokens/all`, `DELETE /api/client-tokens/{id}/admin` — admin-only. + +See [Client API Tokens](../ecosystem/client-api-tokens.md) for the full flow. + +### Search + +- `GET /api/search/roms` — search across metadata providers (IGDB, Moby, SS, Flashpoint, LaunchBox). +- `GET /api/search/cover` — alternate cover search via SteamGridDB. + +### Tasks + +- `GET /api/tasks`, `/api/tasks/status` — what's registered, what's running. +- `POST /api/tasks/run/{task_name}` — trigger on demand (requires `tasks.run`). + +### Configuration + +- `GET /api/config` — current config (parts of it public, parts require auth). +- `POST/DELETE /api/config/system/platforms` — add/remove platform bindings. +- `POST/DELETE /api/config/system/versions` — add/remove version mappings. +- `POST/DELETE /api/config/exclude` — add/remove exclusion rules. + +### Stats + +- `GET /api/stats` — aggregate counts + disk usage. +- `GET /api/stats?include_platform_stats=true` — per-platform breakdown. + +### Feeds + +- `GET /api/feeds/webrcade` — WebRcade. +- `GET /api/feeds/tinfoil` — Nintendo Switch. +- `GET /api/feeds/pkgi/{psvita|psp}/{game|dlc}` — PS Vita / PSP. +- `GET /api/feeds/fpkgi/{ps4|ps5}` — PS4 / PS5. +- `GET /api/feeds/kekatsu/{nds}` — Nintendo DS. +- `GET /api/feeds/pkgj/{psx|psvita|psp}` — legacy pkgj. + +See [Feeds](../reference/feeds.md) for format details per feed. + +### Exports + +- `POST /api/export/gamelist-xml` — ES-DE / Batocera format. +- `POST /api/export/pegasus` — Pegasus frontend format. + +See [Exports](../reference/exports.md). + +### Heartbeat + +- `GET /api/heartbeat` — health + config snapshot. Safe to scrape from uptime monitors. +- `GET /api/heartbeat/metadata/{provider}` — per-provider health. + +### Raw + +- `GET /api/raw/assets/{path}` — direct asset passthrough for advanced integrations. + +### Netplay + +- `GET /api/netplay/list?game_id=...` — active Netplay rooms for a game. +- WebSocket at `/netplay/socket.io` — room coordination. See [WebSockets](websockets.md). + +## WebSockets + +REST isn't the only surface. Two Socket.IO endpoints cover live-update and coordination use cases — [WebSockets](websockets.md). + +## Versioning + +RomM's API follows SemVer along with the rest of RomM: + +- **Breaking changes only in major versions.** Endpoint removal, required-parameter changes, incompatible response-schema shifts. +- **Minor versions add** endpoints, optional parameters, optional response fields. +- **Patch versions fix** bugs without schema changes. + +For reproducible builds, pin the OpenAPI spec version at the same RomM version you target. See [Consuming OpenAPI → Versioning](openapi.md#versioning). + +## See also + +- [API Authentication](api-authentication.md) — auth modes in detail. +- [Consuming OpenAPI](openapi.md) — codegen + schema validation. +- [WebSockets](websockets.md) — Socket.IO endpoints. +- [Client API Tokens](../ecosystem/client-api-tokens.md) — recommended companion-app auth. +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md) — sync endpoints in depth. diff --git a/docs/install/index.md b/docs/install/index.md index 36226b4a..78146895 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -1,10 +1,46 @@ --- -status: placeholder -wave: 1 +title: Install & Deploy +description: Pick a deployment path — Docker Compose, Unraid, Synology, TrueNAS, or Kubernetes. --- # Install & Deploy -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave 1) and - has not been written yet. See the overhaul plan for status and ownership. +RomM is distributed as a Docker image. Every supported deployment runs the same container; the differences are in who manages it. + +## Pick your path + +| If you're on… | Start here | +| --- | --- | +| A Linux server / NAS with Docker | **[Docker Compose](docker-compose.md)** — the canonical reference setup. | +| **Unraid** | [Unraid](unraid.md) — two supported paths (Community Apps template, Docker Compose Manager). | +| **Synology** | [Synology](synology.md) — Container Manager + DSM-specific notes. | +| **TrueNAS SCALE** | [TrueNAS](truenas.md) — App Catalog or YAML install. | +| **Kubernetes** | [Kubernetes](kubernetes.md) — manifest examples, required quirks. | + +If none of those match, start with [Docker Compose](docker-compose.md) and adapt. + +## Foundational pieces + +Regardless of host platform, you'll make the same handful of decisions: + +- **[Image variant](image-variants.md)** — `full` (default, includes in-browser emulators) or `slim` (headless, smaller footprint). +- **[Database](databases.md)** — MariaDB (default), MySQL, PostgreSQL, or SQLite (dev-only). +- **[Redis / Valkey](redis-or-valkey.md)** — required for sessions + task queue. Embedded or external. +- **[Reverse proxy](reverse-proxy.md)** — Caddy, nginx, Traefik, or NPM. HTTPS required for OIDC and PWA install. +- **[Backup & restore](backup-and-restore.md)** — don't skip. Test the restore before you need it. + +## Minimum reading before going live + +1. [Quick Start](../getting-started/quick-start.md) — 15-minute happy-path walkthrough. +2. [Docker Compose](docker-compose.md) — production-oriented reference compose. +3. [Reverse Proxy](reverse-proxy.md) — pick a TLS-terminating proxy. +4. [Backup & Restore](backup-and-restore.md) — nightly backup + restore drill. + +Then come back here when you're ready to pick a platform-specific guide, or read [Administration](../administration/index.md) to set up your first users, metadata, and scheduled tasks. + +## After you're up and running + +- **Populate the library** — [Your First Scan](../getting-started/first-scan.md). +- **Add users** — [Invitations & Registration](../administration/invitations-and-registration.md). +- **Configure metadata providers** — [Metadata Providers](../administration/metadata-providers.md). +- **Upgrading from 4.x?** — [Upgrading to 5.0](../releases/upgrading-to-5.0.md). diff --git a/docs/platforms/supported-platforms.md b/docs/platforms/supported-platforms.md index 41a03c16..5bc0cc99 100644 --- a/docs/platforms/supported-platforms.md +++ b/docs/platforms/supported-platforms.md @@ -1,13 +1,51 @@ --- -status: placeholder -wave: 2 +title: Supported Platforms +description: Every platform RomM recognises, with metadata-provider coverage and in-browser-play flags. --- # Supported Platforms -!!! warning "Placeholder — RomM 5.0 docs overhaul" - The full table will be generated by `docs/scripts/gen_platforms.py` once a - 5.0 SHA is pinned in `docs/scripts/sources.toml`. The current `docs/Platforms-and-Players/Supported-Platforms.md` - remains the source for now. +RomM ships support for ~400 platforms. "Support" means: + +1. **RomM recognises the folder name** and maps it to a canonical platform. +2. **At least one metadata provider** has coverage. +3. **EmulatorJS** may have a playable core (flagged per platform in the table below). + +Your folder name has to match the **platform slug** in the table. If yours differs, use [`system.platforms`](../reference/configuration-file.md#systemplatforms) in `config.yml` to remap — see [Folder Structure](../getting-started/folder-structure.md). + +## Platform slugs + coverage --8<-- "supported-platforms.md" + +## What the columns mean + +- **Slug** — the folder name RomM expects. Matches the IGDB platform slug where possible. +- **Name** — the human-readable platform name. +- **Providers** — which metadata providers have at least partial coverage. See [Metadata Providers](../administration/metadata-providers.md). +- **EmulatorJS** — a playable in-browser core exists. See [EmulatorJS Configuration](emulatorjs-config.md). +- **Firmware** — platform needs BIOS files for emulation. See [Firmware by Platform](firmware-by-platform.md). + +## Platform not listed? + +Two options: + +- **[Custom Platforms](custom-platforms.md)** — add an unknown platform. RomM will recognise the folder but won't have metadata or emulator support. +- **Map to an existing slug** via `config.yml` if yours is a naming variant of something RomM already supports (e.g. `super_nintendo` → `snes`). + +Open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) if you believe a platform should be added to the built-in list. + +## Where the table comes from + +Generated by `docs/scripts/gen_platforms.py` against the upstream platform registry at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). Regenerate locally with: + +```sh +uv run python docs/scripts/gen_platforms.py +``` + +## See also + +- [Folder Structure](../getting-started/folder-structure.md) — how platform slugs map to on-disk folders. +- [Custom Platforms](custom-platforms.md) — adding platforms outside the built-in list. +- [Metadata Providers](../administration/metadata-providers.md) — provider coverage deep-dive. +- [In-Browser Play](../using/in-browser-play.md) — EmulatorJS core catalogue. +- [Firmware by Platform](firmware-by-platform.md) — per-platform BIOS requirements. diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 9cb4d9ed..eb79bc12 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -1,16 +1,72 @@ --- -status: placeholder -wave: 1 +title: Environment Variables +description: Every environment variable RomM reads, grouped by what it controls. --- # Environment Variables -!!! warning "Placeholder — RomM 5.0 docs overhaul" - Narrative content for this page is still being written for Wave 1. The - table below is generated automatically from - [`rommapp/romm`'s `env.template`][src] at the SHA pinned in - `docs/scripts/sources.toml`. +Everything RomM does that's not in [`config.yml`](configuration-file.md) is driven by env vars. Set them on the `romm` service in your compose file, as Unraid / Synology / TrueNAS container env vars, or on your Kubernetes deployment. - [src]: https://github.com/rommapp/romm/blob/master/env.template +This page is the **authoritative lookup** — every var RomM reads. The table is generated directly from [`rommapp/romm`'s `env.template`][src] at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). When RomM adds an env var, the next docs bump re-runs the generator and this page updates. + +[src]: https://github.com/rommapp/romm/blob/master/env.template + +## Setting env vars + +### Docker Compose + +```yaml +services: + romm: + environment: + - ROMM_AUTH_SECRET_KEY=abcd1234... + - DB_PASSWD=secure-password + # ... +``` + +Or from a `.env` file next to your compose: + +```yaml +services: + romm: + env_file: + - .env +``` + +### Secrets + +Don't embed `ROMM_AUTH_SECRET_KEY`, DB passwords, or provider API keys directly in a committed compose file. Use: + +- A `.env` that's `.gitignore`d. +- Docker secrets (`ROMM_AUTH_SECRET_KEY_FILE` reads from a mounted file). +- Your orchestrator's secret store (K8s Secrets, HashiCorp Vault, AWS Secrets Manager). + +## Essential variables + +You'll always set these: + +| Variable | Purpose | +| --- | --- | +| `ROMM_AUTH_SECRET_KEY` | JWT signing key. Generate with `openssl rand -hex 32`. **Never rotate lightly** — breaks all sessions. | +| `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | +| `ROMM_DB_DRIVER` | `mariadb` (default), `mysql`, `postgresql`, or `sqlite`. See [Databases](../install/databases.md). | +| `ROMM_BASE_URL` | Public URL behind your reverse proxy. Needed for correct OIDC redirects, QR codes, and invite links. | + +For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../administration/metadata-providers.md). For OIDC, see [OIDC Setup](../administration/oidc/index.md). + +## When env vars are read + +- **Startup.** Most vars are consumed once at container start. Change requires `docker compose up -d` to apply. +- **Per-request.** A handful of feature toggles (`KIOSK_MODE`, `DISABLE_USERPASS_LOGIN`) are re-checked per request — still, restart is safest to avoid partial-state caching. +- **Never at runtime.** There's no reload-config endpoint. + +## Full reference --8<-- "env-vars.md" + +## See also + +- [Configuration File](configuration-file.md) — everything that lives in `config.yml` rather than env vars. +- [Scheduled Tasks](scheduled-tasks.md) — cron-controlling env vars in context. +- [Authentication](../administration/authentication.md) — auth-related env vars in narrative form. +- [Metadata Providers](../administration/metadata-providers.md) — per-provider credential env vars. diff --git a/docs/reference/scheduled-tasks.md b/docs/reference/scheduled-tasks.md index f01ed106..877f4819 100644 --- a/docs/reference/scheduled-tasks.md +++ b/docs/reference/scheduled-tasks.md @@ -1,12 +1,53 @@ --- -status: placeholder -wave: 1 +title: Scheduled Tasks +description: Every background task RomM runs, with default schedule, env var, and purpose. --- # Scheduled Tasks -!!! warning "Placeholder — RomM 5.0 docs overhaul" - Narrative content for this page is still being written for Wave 1. The - table below is generated by `docs/scripts/gen_scheduled_tasks.py`. +RomM runs background work via **RQ** (Redis Queue). Tasks fall into three categories: + +- **Scheduled** — cron-driven, fire on their own. +- **Watcher** — triggered by filesystem events. +- **Manual** — admin-triggered from the UI or API. + +This page is the **lookup reference** — every task, its cron default, its env var, and a one-line purpose. For the narrative (how to tune cadence, how to trigger manually, how to monitor), see [Administration → Scheduled Tasks](../administration/scheduled-tasks.md). + +## Full task table --8<-- "scheduled-tasks.md" + +## Setting the cadence + +Every scheduled task takes a standard 5-field cron expression: + +```text +minute hour day-of-month month day-of-week +``` + +Examples: + +- `0 3 * * *` — 3 AM daily. +- `0 */6 * * *` — every 6 hours. +- `*/30 * * * *` — every 30 minutes. +- `0 2 * * 0` — 2 AM every Sunday. + +Set the matching env var from the table above, restart the container, and the scheduler picks up the new cadence on startup. + +## Disabling a task + +Two approaches: + +- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`) — cleanest. +- **Cron-it-off.** Set the cron to a moment that effectively never fires. `0 0 31 2 *` (Feb 31st) works. + +## Where the table comes from + +Generated by `docs/scripts/gen_scheduled_tasks.py` against the task registry at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). When RomM adds or renames a task, the next docs release regenerates the table. + +## See also + +- [Administration → Scheduled Tasks](../administration/scheduled-tasks.md) — full narrative, per-task purpose, monitoring, tuning. +- [Environment Variables](environment-variables.md) — all task-related env vars in context. +- [Scanning & Watcher](../administration/scanning-and-watcher.md) — the scan and watcher tasks in depth. +- [SSH Sync](../administration/ssh-sync.md) — the push-pull sync task in depth. From fdcd25e25c053b93a49f69f1cd94ba7b61b1279f Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 16:05:33 -0400 Subject: [PATCH 023/121] remove scaffold --- .gitignore | 2 + docs/scripts/scaffold_ia.py | 167 ------------------------------------ 2 files changed, 2 insertions(+), 167 deletions(-) delete mode 100644 docs/scripts/scaffold_ia.py diff --git a/.gitignore b/.gitignore index c7ba7317..341a8182 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ site .cache .DS_Store *.kate-swp +__pycache__ +*.pyc diff --git a/docs/scripts/scaffold_ia.py b/docs/scripts/scaffold_ia.py deleted file mode 100644 index e2c3c893..00000000 --- a/docs/scripts/scaffold_ia.py +++ /dev/null @@ -1,167 +0,0 @@ -"""One-shot scaffolder: create placeholder pages for the v5.0 IA. - -Idempotent: pages that already exist are left alone. Used during the -initial overhaul setup; can be deleted once the IA is stable. - -Run: - uv run python docs/scripts/scaffold_ia.py -""" - -from __future__ import annotations - -from pathlib import Path - -DOCS = Path(__file__).resolve().parents[1] - -# (relative_path, page_title, wave) -- wave is informational, surfaced in the stub. -PAGES: list[tuple[str, str, int]] = [ - # Getting Started - ("getting-started/quick-start.md", "Quick Start", 1), - ("getting-started/folder-structure.md", "Folder Structure", 1), - ("getting-started/first-scan.md", "Your First Scan", 1), - ("getting-started/concepts.md", "Core Concepts", 1), - ("getting-started/what-is-new-in-5.md", "What's New in 5.0", 1), - # Install & Deploy - ("install/index.md", "Install & Deploy", 1), - ("install/docker-compose.md", "Docker Compose", 1), - ("install/image-variants.md", "Image Variants", 1), - ("install/databases.md", "Databases", 1), - ("install/redis-or-valkey.md", "Redis or Valkey", 1), - ("install/reverse-proxy.md", "Reverse Proxy", 1), - ("install/unraid.md", "Unraid", 1), - ("install/synology.md", "Synology", 1), - ("install/truenas.md", "TrueNAS", 1), - ("install/kubernetes.md", "Kubernetes", 1), - ("install/backup-and-restore.md", "Backup & Restore", 1), - # Administration - ("administration/index.md", "Administration", 1), - ("administration/users-and-roles.md", "Users & Roles", 1), - ("administration/invitations-and-registration.md", "Invitations & Registration", 1), - ("administration/authentication.md", "Authentication", 1), - ("administration/metadata-providers.md", "Metadata Providers", 1), - ("administration/scanning-and-watcher.md", "Scanning & Watcher", 1), - ("administration/scheduled-tasks.md", "Scheduled Tasks", 1), - ("administration/server-stats.md", "Server Stats", 1), - ("administration/observability.md", "Observability", 1), - ("administration/firmware-management.md", "Firmware Management", 1), - ("administration/ssh-sync.md", "SSH Sync", 1), - ("administration/administration-page.md", "Administration Page", 1), - # Administration / OIDC - ("administration/oidc/index.md", "OIDC Setup", 1), - ("administration/oidc/authelia.md", "OIDC with Authelia", 1), - ("administration/oidc/authentik.md", "OIDC with Authentik", 1), - ("administration/oidc/keycloak.md", "OIDC with Keycloak", 1), - ("administration/oidc/pocketid.md", "OIDC with PocketID", 1), - ("administration/oidc/zitadel.md", "OIDC with Zitadel", 1), - # Using RomM - ("using/index.md", "Using RomM", 2), - ("using/library.md", "Library", 2), - ("using/collections.md", "Collections", 2), - ("using/smart-collections.md", "Smart Collections", 2), - ("using/virtual-collections.md", "Virtual Collections", 2), - ("using/downloads.md", "Downloads", 2), - ("using/uploads.md", "Uploads", 2), - ("using/in-browser-play.md", "In-Browser Play", 2), - ("using/saves-and-states.md", "Saves & States", 2), - ("using/retroachievements.md", "RetroAchievements", 2), - ("using/rom-patcher.md", "ROM Patcher", 2), - ("using/netplay.md", "Netplay", 2), - ("using/console-mode.md", "Console Mode", 2), - ("using/pwa.md", "Install as PWA", 2), - ("using/mobile-and-tv.md", "Mobile & TV", 2), - ("using/account-and-profile.md", "Account & Profile", 2), - ("using/languages.md", "Languages", 2), - # Platforms & Players - ("platforms/index.md", "Platforms & Players", 2), - ("platforms/supported-platforms.md", "Supported Platforms", 2), - ("platforms/custom-platforms.md", "Custom Platforms", 2), - ("platforms/ms-dos.md", "MS-DOS", 2), - ("platforms/emulatorjs-config.md", "EmulatorJS Configuration", 2), - ("platforms/ruffle-config.md", "Ruffle Configuration", 2), - ("platforms/firmware-by-platform.md", "Firmware by Platform", 2), - # Ecosystem - ("ecosystem/index.md", "Integrations & Ecosystem", 3), - ("ecosystem/argosy.md", "Argosy Launcher", 3), - ("ecosystem/grout.md", "Grout", 3), - ("ecosystem/playnite-plugin.md", "Playnite Plugin", 3), - ("ecosystem/muos-app.md", "muOS App", 3), - ("ecosystem/tinfoil.md", "Tinfoil", 3), - ("ecosystem/pkgj.md", "pkgj", 3), - ("ecosystem/fpkgi.md", "fpkgi", 3), - ("ecosystem/kekatsu.md", "Kekatsu", 3), - ("ecosystem/webrcade.md", "WebRcade", 3), - ("ecosystem/community-apps.md", "Community Apps", 3), - ("ecosystem/device-sync-protocol.md", "Device Sync Protocol", 3), - ("ecosystem/client-api-tokens.md", "Client API Tokens", 3), - ("ecosystem/igir.md", "Igir Collection Manager", 3), - # Developers - ("developers/index.md", "API & Development", 1), - ("developers/api-reference.md", "API Reference", 1), - ("developers/api-authentication.md", "API Authentication", 1), - ("developers/websockets.md", "WebSockets", 3), - ("developers/openapi.md", "Consuming OpenAPI", 3), - ("developers/development-setup.md", "Development Setup", 1), - ("developers/architecture.md", "Architecture", 3), - ("developers/contributing.md", "Contributing", 1), - ("developers/i18n.md", "Translations (i18n)", 3), - ("developers/releasing.md", "Releasing", 3), - # Reference - ("reference/environment-variables.md", "Environment Variables", 1), - ("reference/configuration-file.md", "Configuration File", 1), - ("reference/scheduled-tasks.md", "Scheduled Tasks Reference", 1), - ("reference/exports.md", "Exports", 3), - ("reference/feeds.md", "Feeds", 3), - ("reference/ports-and-endpoints.md", "Ports & Endpoints", 3), - ("reference/glossary.md", "Glossary", 3), - # Troubleshooting - ("troubleshooting/index.md", "Troubleshooting", 1), - ("troubleshooting/scanning.md", "Scanning", 1), - ("troubleshooting/authentication.md", "Authentication", 1), - ("troubleshooting/synology.md", "Synology", 1), - ("troubleshooting/kubernetes.md", "Kubernetes", 1), - ("troubleshooting/in-browser-play.md", "In-Browser Play", 2), - ("troubleshooting/netplay.md", "Netplay", 2), - ("troubleshooting/sync.md", "Device Sync", 2), - ("troubleshooting/miscellaneous.md", "Miscellaneous", 1), - # Releases - ("releases/index.md", "Release Notes & Migration", 1), - ("releases/upgrading-to-5.0.md", "Upgrading to 5.0", 1), - ("releases/upgrading-to-3.0.md", "Upgrading to 3.0", 1), - ("releases/changelog.md", "Changelog", 1), - # About - ("about/faqs.md", "FAQs", 3), - ("about/brand-guidelines.md", "Brand Guidelines", 3), - ("about/license.md", "License", 3), - ("about/credits.md", "Credits", 3), -] - - -STUB = """\ ---- -status: placeholder -wave: {wave} ---- - -# {title} - -!!! warning "Placeholder — RomM 5.0 docs overhaul" - This page is part of the RomM 5.0 documentation overhaul (Wave {wave}) and - has not been written yet. See the overhaul plan for status and ownership. -""" - - -def main() -> int: - created = 0 - for rel, title, wave in PAGES: - path = DOCS / rel - if path.exists(): - continue - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(STUB.format(title=title, wave=wave), encoding="utf-8") - created += 1 - print(f"Created {created} placeholder pages ({len(PAGES) - created} already existed).") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) From 1d6abc65ef295c7410a710c2c0b92e95b8e69dd5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 20:19:28 +0000 Subject: [PATCH 024/121] docs: update gen_env_vars.py for new env.template format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit env.template switched from the old "pre-variable comment lines" style to an inline format: `VAR=default # description [REQUIRED]?`. Rewrite of the parser: - **Section headers** — `# Section Name` comment lines (no `=`) at any capitalization. Used to group rows in the output. - **Variable lines** — `KEY=VALUE # COMMENT`. The inline-comment separator is 2+ spaces before `#`. Values may contain spaces (cron expressions, etc.) and may be double-quoted (quotes are stripped on parse). - **Required flag** — any `[REQUIRED]` substring in the comment marks the variable. Renders as **required** in the Default column instead of the literal default value. Output format unchanged otherwise (markdown table, one section per heading). Required vars are now clearly distinguishable from simply -unset vars. Verified end-to-end against a representative slice of the new env.template (core app, database with required fields, scans&tasks with a cron value, integrations with a quoted string, auth with the required secret key) — parser produces expected rows and the render output is clean. --- docs/scripts/gen_env_vars.py | 101 +++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/docs/scripts/gen_env_vars.py b/docs/scripts/gen_env_vars.py index 6f24e316..a997fe39 100644 --- a/docs/scripts/gen_env_vars.py +++ b/docs/scripts/gen_env_vars.py @@ -7,6 +7,20 @@ Run manually: uv run python docs/scripts/gen_env_vars.py + +The env.template format this parses: + + # Section Name + VAR_NAME=default_value # Description + VAR_WITH_REQUIRED= # Description [REQUIRED] + VAR_QUOTED="value with spaces" # Description + +Rules: +- Lines starting with `#` and no `=` are section headers. +- Variable lines are `KEY=VALUE # COMMENT` with two-or-more spaces + before the `#` that introduces the inline comment. +- `[REQUIRED]` anywhere in the comment flags the variable as required. +- Surrounding double quotes on the default value are stripped. """ from __future__ import annotations @@ -18,43 +32,66 @@ from _sources import fetch_text, romm_raw_url, write_snippet -SECTION_RE = re.compile(r"^#\s*([A-Z][A-Z0-9 _/\-&]+?)\s*#*\s*$") -COMMENT_RE = re.compile(r"^#\s?(.*)$") -ASSIGN_RE = re.compile(r"^([A-Z][A-Z0-9_]*)=(.*)$") +# KEY=VALUE, followed optionally by ` # comment`. Value may contain spaces. +VAR_LINE_RE = re.compile(r"^([A-Z][A-Z0-9_]*)=(.*)$") +# The inline-comment separator: 2+ spaces, then `#`, then optional space. +INLINE_COMMENT_RE = re.compile(r"\s{2,}#\s*(.*)$") + + +def parse_var(line: str) -> dict | None: + m = VAR_LINE_RE.match(line) + if not m: + return None + + name = m.group(1) + rest = m.group(2) + + comment_match = INLINE_COMMENT_RE.search(rest) + if comment_match: + comment = comment_match.group(1).strip() + value = rest[: comment_match.start()] + else: + comment = "" + value = rest + + value = value.strip() + # Strip surrounding double quotes from the default value. + if len(value) >= 2 and value.startswith('"') and value.endswith('"'): + value = value[1:-1] + + required = "[REQUIRED]" in comment + description = comment.replace("[REQUIRED]", "").strip() + + return { + "name": name, + "default": value, + "description": description, + "required": required, + } def parse(env_template: str) -> list[dict]: section = "General" - pending_comments: list[str] = [] rows: list[dict] = [] for raw in env_template.splitlines(): line = raw.rstrip() if not line.strip(): - pending_comments = [] continue - sec = SECTION_RE.match(line) - if sec and len(line) > 4 and line.startswith("#") and "=" not in line: - section = sec.group(1).strip().title() - pending_comments = [] - continue + # Variable line — has `=` and starts with an uppercase letter. + if "=" in line and line[:1].isalpha() and line[:1].isupper(): + row = parse_var(line) + if row: + row["section"] = section + rows.append(row) + continue - cmt = COMMENT_RE.match(line) - if cmt: - pending_comments.append(cmt.group(1).strip()) - continue - - m = ASSIGN_RE.match(line) - if m: - name, default = m.group(1), m.group(2).strip() - rows.append({ - "section": section, - "name": name, - "default": default, - "description": " ".join(c for c in pending_comments if c), - }) - pending_comments = [] + # Section header — comment line with no `=`. + if line.startswith("#"): + text = line.lstrip("#").strip() + if text and "=" not in text: + section = text return rows @@ -67,16 +104,23 @@ def render(rows: Iterable[dict]) -> str: out: list[str] = [] out.append("") out.append("") + for section, items in grouped.items(): out.append(f"### {section}") out.append("") out.append("| Variable | Default | Description |") out.append("| --- | --- | --- |") for r in items: - default = r["default"] if r["default"] else "_(unset)_" + if r["required"]: + default = "**required**" + elif r["default"]: + default = f"`{r['default']}`" + else: + default = "_(unset)_" desc = r["description"] or "—" - out.append(f"| `{r['name']}` | `{default}` | {desc} |") + out.append(f"| `{r['name']}` | {default} | {desc} |") out.append("") + return "\n".join(out) @@ -89,7 +133,8 @@ def main() -> int: return 1 snippet = render(rows) out = write_snippet("env-vars.md", snippet) - print(f"Wrote {len(rows)} env vars to {out}") + required = sum(1 for r in rows if r["required"]) + print(f"Wrote {len(rows)} env vars ({required} required) to {out}") return 0 From e4a06b9c7768b0e0d9a5abe4901a1b0dc2be6858 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 20:22:36 +0000 Subject: [PATCH 025/121] docs: gen_env_vars: split required into its own column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Default column: empty when unset (no more `_(unset)_` filler). - New Required column, centred, uses `✓` for required vars, empty otherwise. - Description column unchanged. Cleaner-looking tables, and filtering for required-only vars is now a straightforward scan of the Required column instead of parsing a bolded-string fallback in the Default column. --- docs/scripts/gen_env_vars.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/scripts/gen_env_vars.py b/docs/scripts/gen_env_vars.py index a997fe39..15a537f6 100644 --- a/docs/scripts/gen_env_vars.py +++ b/docs/scripts/gen_env_vars.py @@ -108,17 +108,15 @@ def render(rows: Iterable[dict]) -> str: for section, items in grouped.items(): out.append(f"### {section}") out.append("") - out.append("| Variable | Default | Description |") - out.append("| --- | --- | --- |") + out.append("| Variable | Default | Required | Description |") + out.append("| --- | --- | :---: | --- |") for r in items: - if r["required"]: - default = "**required**" - elif r["default"]: - default = f"`{r['default']}`" - else: - default = "_(unset)_" + default = f"`{r['default']}`" if r["default"] else "" + required = "✓" if r["required"] else "" desc = r["description"] or "—" - out.append(f"| `{r['name']}` | {default} | {desc} |") + out.append( + f"| `{r['name']}` | {default} | {required} | {desc} |" + ) out.append("") return "\n".join(out) From 77a2ae9a81660e2be7d394d8e0b19b3482d1f06b Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 16:34:58 -0400 Subject: [PATCH 026/121] generate env vars --- docs/reference/environment-variables.md | 1 - docs/resources/snippets/env-vars.md | 283 ++++++++++++++++-------- docs/scripts/gen_env_vars.py | 2 +- 3 files changed, 186 insertions(+), 100 deletions(-) diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index eb79bc12..936c833e 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -50,7 +50,6 @@ You'll always set these: | `ROMM_AUTH_SECRET_KEY` | JWT signing key. Generate with `openssl rand -hex 32`. **Never rotate lightly** — breaks all sessions. | | `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | | `ROMM_DB_DRIVER` | `mariadb` (default), `mysql`, `postgresql`, or `sqlite`. See [Databases](../install/databases.md). | -| `ROMM_BASE_URL` | Public URL behind your reverse proxy. Needed for correct OIDC redirects, QR codes, and invite links. | For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../administration/metadata-providers.md). For OIDC, see [OIDC Setup](../administration/oidc/index.md). diff --git a/docs/resources/snippets/env-vars.md b/docs/resources/snippets/env-vars.md index 46f2ec7c..688c645d 100644 --- a/docs/resources/snippets/env-vars.md +++ b/docs/resources/snippets/env-vars.md @@ -1,100 +1,187 @@ -### General - -| Variable | Default | Description | -| --- | --- | --- | -| `ROMM_BASE_PATH` | `/path/to/romm_mock` | — | -| `ROMM_TMP_PATH` | `_(unset)_` | — | -| `KIOSK_MODE` | `false` | — | -| `IGDB_CLIENT_ID` | `_(unset)_` | IGDB credentials | -| `IGDB_CLIENT_SECRET` | `_(unset)_` | — | -| `MOBYGAMES_API_KEY` | `_(unset)_` | Mobygames | -| `SCREENSCRAPER_USER` | `_(unset)_` | Screenscraper | -| `SCREENSCRAPER_PASSWORD` | `_(unset)_` | — | -| `STEAMGRIDDB_API_KEY` | `_(unset)_` | SteamGridDB | -| `RETROACHIEVEMENTS_API_KEY` | `_(unset)_` | RetroAchievements | -| `PLAYMATCH_API_ENABLED` | `_(unset)_` | Playmatch | -| `LAUNCHBOX_API_ENABLED` | `_(unset)_` | LaunchBox | -| `HASHEOUS_API_ENABLED` | `_(unset)_` | Hasheous | -| `FLASHPOINT_API_ENABLED` | `_(unset)_` | Flashpoint Project | -| `HLTB_API_ENABLED` | `_(unset)_` | HowLongToBeat | -| `TGDB_API_ENABLED` | `_(unset)_` | TheGamesDB | -| `ROMM_DB_DRIVER` | `mariadb` | Database config | -| `DB_HOST` | `127.0.0.1` | — | -| `DB_PORT` | `3306` | — | -| `DB_NAME` | `romm` | — | -| `DB_USER` | `romm` | — | -| `DB_PASSWD` | `_(unset)_` | — | -| `DB_ROOT_PASSWD` | `_(unset)_` | — | -| `REDIS_HOST` | `127.0.0.1` | Redis config | -| `REDIS_PORT` | `6379` | — | -| `POSTGRES_DB` | `authentik` | Authentik | -| `POSTGRES_USER` | `authentik` | — | -| `POSTGRES_PASSWORD` | `authentik` | — | -| `AUTHENTIK_SECRET_KEY` | `_(unset)_` | — | -| `AUTHENTIK_BOOTSTRAP_PASSWORD` | `_(unset)_` | — | -| `ROMM_AUTH_SECRET_KEY` | `_(unset)_` | Authentication | -| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | `_(unset)_` | — | -| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | `_(unset)_` | — | -| `SESSION_MAX_AGE_SECONDS` | `_(unset)_` | — | -| `DISABLE_DOWNLOAD_ENDPOINT_AUTH` | `_(unset)_` | Disable auth on download endpoint for 3rd party support | -| `DISABLE_CSRF_PROTECTION` | `_(unset)_` | Disable CSRF protection for development and testing purposes | -| `DISABLE_USERPASS_LOGIN` | `_(unset)_` | Disable username + passsword login when using OIDC login | -| `DISABLE_SETUP_WIZARD` | `_(unset)_` | — | -| `INVITE_TOKEN_EXPIRY_SECONDS` | `600` | — | -| `OIDC_ENABLED` | `_(unset)_` | OpenID Connect (Authentik, Authelia, etc.) | -| `OIDC_AUTOLOGIN` | `_(unset)_` | — | -| `OIDC_PROVIDER` | `_(unset)_` | — | -| `OIDC_CLIENT_ID` | `_(unset)_` | — | -| `OIDC_CLIENT_SECRET` | `_(unset)_` | — | -| `OIDC_REDIRECT_URI` | `_(unset)_` | — | -| `OIDC_SERVER_APPLICATION_URL` | `_(unset)_` | — | -| `OIDC_SERVER_METADATA_URL` | `_(unset)_` | — | -| `OIDC_CLAIM_ROLES` | `_(unset)_` | — | -| `OIDC_ROLE_VIEWER` | `_(unset)_` | — | -| `OIDC_ROLE_EDITOR` | `_(unset)_` | — | -| `OIDC_ROLE_ADMIN` | `_(unset)_` | — | -| `OIDC_TLS_CACERTFILE` | `_(unset)_` | — | -| `OIDC_USERNAME_ATTRIBUTE` | `preferred_username` | — | -| `OIDC_RP_INITIATED_LOGOUT` | `_(unset)_` | — | -| `OIDC_END_SESSION_ENDPOINT` | `_(unset)_` | — | -| `ENABLE_RESCAN_ON_FILESYSTEM_CHANGE` | `true` | Filesystem watcher (optional) | -| `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` | `5` | — | -| `TASK_TIMEOUT` | `300` | Tasks (optional) | -| `TASK_RESULT_TTL` | `86400` | — | -| `SEVEN_ZIP_TIMEOUT` | `60` | — | -| `ENABLE_SCHEDULED_RESCAN` | `true` | — | -| `SCHEDULED_RESCAN_CRON` | `0 3 * * *` | — | -| `ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB` | `true` | — | -| `SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON` | `0 4 * * *` | — | -| `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA` | `true` | — | -| `SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON` | `0 4 * * *` | — | -| `ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP` | `true` | — | -| `SCHEDULED_CONVERT_IMAGES_TO_WEBP_CRON` | `0 4 * * *` | — | -| `ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC` | `true` | — | -| `SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC_CRON` | `0 4 * * *` | — | -| `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` | `30` | — | -| `DISABLE_EMULATOR_JS` | `false` | In-browser emulation | -| `DISABLE_RUFFLE_RS` | `false` | — | -| `YOUTUBE_BASE_URL` | `https://www.youtube.com` | YouTube alternatives (Piped, Invidious, etc.) | -| `TINFOIL_WELCOME_MESSAGE` | `"RomM Switch Library"` | Switch Tinfoil | -| `LOGLEVEL` | `DEBUG` | Logging | -| `FORCE_COLOR` | `_(unset)_` | — | -| `NO_COLOR` | `_(unset)_` | — | -| `WEB_SERVER_CONCURRENCY` | `2` | Web server (optional) Workers -> (2 × CPU cores) + 1 | -| `WEB_SERVER_TIMEOUT` | `300` | — | -| `WEB_SERVER_KEEPALIVE` | `2` | — | -| `WEB_SERVER_MAX_REQUESTS` | `1000` | — | -| `WEB_SERVER_MAX_REQUESTS_JITTER` | `100` | — | -| `WEB_SERVER_WORKER_CONNECTIONS` | `1000` | — | -| `WEB_SERVER_GUNICORN_WAIT_SECONDS` | `30` | — | -| `IPV4_ONLY` | `false` | — | -| `SCAN_TIMEOUT` | `_(unset)_` | Redis Workers | -| `SCAN_WORKERS` | `_(unset)_` | — | -| `DEV_MODE` | `true` | Development only | -| `DEV_HOST` | `127.0.0.1` | — | -| `DEV_PORT` | `5000` | — | -| `DEV_HTTPS` | `false` | — | -| `DEV_SQL_ECHO` | `false` | — | -| `SENTRY_DSN` | `_(unset)_` | — | +### Core Application + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `ROMM_BASE_PATH` | `/romm` | | Base folder path for library, resources and assets | +| `ROMM_TMP_PATH` | | | Custom temporary directory path | +| `ROMM_BASE_URL` | `http://0.0.0.0` | | Base URL used when rendering container log links | +| `ROMM_PORT` | `8080` | | Port on which the application listens | +| `KIOSK_MODE` | `false` | | Read-only mode for public displays or kiosks | + +### Database + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `ROMM_DB_DRIVER` | `mariadb` | | Database driver to use (mariadb, mysql, postgresql) | +| `DB_HOST` | | `✓` | Host name of the database instance | +| `DB_PORT` | `3306` | | Port number of the database instance | +| `DB_NAME` | `romm` | | Database name (should match MYSQL_DATABASE in MariaDB) | +| `DB_USER` | | `✓` | Database username (should match MARIADB_USER in MariaDB) | +| `DB_PASSWD` | | `✓` | Database password (should match MARIADB_PASSWORD in MariaDB) | +| `DB_ROOT_PASSWD` | | | Database root user password (only used by the bundled MariaDB container) | +| `DB_QUERY_JSON` | | | Extra query parameters for the database connection, as JSON | + +### Redis/Valkey + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `REDIS_HOST` | `127.0.0.1` | | Host name of the Redis/Valkey instance | +| `REDIS_PORT` | `6379` | | Port number of the Redis/Valkey instance | +| `REDIS_USERNAME` | | | Username for the Redis/Valkey instance | +| `REDIS_PASSWORD` | | | Password for the Redis/Valkey instance | +| `REDIS_DB` | `0` | | Database number for the Redis/Valkey instance | +| `REDIS_SSL` | `false` | | Enable SSL (rediss://) for the Redis/Valkey connection | + +### Authentication + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `ROMM_AUTH_SECRET_KEY` | | `✓` | App secret, generate with `openssl rand -hex 32` | +| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | `1800` | | Access token lifetime in seconds | +| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | `604800` | | Refresh token lifetime in seconds | +| `SESSION_MAX_AGE_SECONDS` | `1209600` | | Maximum age of a session in seconds | +| `INVITE_TOKEN_EXPIRY_SECONDS` | `600` | | Invite token lifetime in seconds | +| `DISABLE_DOWNLOAD_ENDPOINT_AUTH` | `false` | | Disable auth on the download endpoint for WebRcade/Tinfoil | +| `DISABLE_CSRF_PROTECTION` | `false` | | Disable CSRF protection (not recommended) | +| `DISABLE_USERPASS_LOGIN` | `false` | | Disable username/password login when using OIDC | +| `DISABLE_SETUP_WIZARD` | `false` | | Skip the first-boot setup wizard | + +### OpenID Connect + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `OIDC_ENABLED` | `false` | | Enable OpenID Connect authentication | +| `OIDC_AUTOLOGIN` | `false` | | Skip the OIDC button on the login page and auto-redirect | +| `OIDC_PROVIDER` | | | Name of the OIDC provider in use | +| `OIDC_CLIENT_ID` | | | Client ID for OIDC authentication | +| `OIDC_CLIENT_SECRET` | | | Client secret for OIDC authentication | +| `OIDC_REDIRECT_URI` | | | Absolute redirect URI for OIDC authentication | +| `OIDC_SERVER_APPLICATION_URL` | | | Absolute URL of the OIDC server application | +| `OIDC_SERVER_METADATA_URL` | | | URL to the OIDC provider metadata endpoint | +| `OIDC_CLAIM_ROLES` | | | OIDC claim containing user roles | +| `OIDC_ROLE_VIEWER` | | | Role value mapping to viewer permissions | +| `OIDC_ROLE_EDITOR` | | | Role value mapping to editor permissions | +| `OIDC_ROLE_ADMIN` | | | Role value mapping to admin permissions | +| `OIDC_TLS_CACERTFILE` | | | Path to file containing trusted CA certificates | +| `OIDC_USERNAME_ATTRIBUTE` | `preferred_username` | | Attribute on OIDC user info used as the username | +| `OIDC_RP_INITIATED_LOGOUT` | `false` | | Enable RP-initiated logout flow | +| `OIDC_END_SESSION_ENDPOINT` | | | OIDC end-session endpoint override URL | + +### Metadata Providers + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `IGDB_CLIENT_ID` | | | Client ID for the IGDB API | +| `IGDB_CLIENT_SECRET` | | | Client secret for the IGDB API | +| `MOBYGAMES_API_KEY` | | | MobyGames secret API key | +| `SCREENSCRAPER_USER` | | | Screenscraper username | +| `SCREENSCRAPER_PASSWORD` | | | Screenscraper password | +| `STEAMGRIDDB_API_KEY` | | | SteamGridDB secret API key | +| `RETROACHIEVEMENTS_API_KEY` | | | RetroAchievements secret API key | +| `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` | `30` | | RetroAchievements metadata cache refresh interval in days | +| `PLAYMATCH_API_ENABLED` | `false` | | Enable PlayMatch API integration | +| `LAUNCHBOX_API_ENABLED` | `false` | | Enable LaunchBox API integration | +| `HASHEOUS_API_ENABLED` | `false` | | Enable Hasheous API integration | +| `FLASHPOINT_API_ENABLED` | `false` | | Enable Flashpoint API integration | +| `HLTB_API_ENABLED` | `false` | | Enable HowLongToBeat API integration | +| `TGDB_API_ENABLED` | `false` | | Enable TheGamesDB API integration | + +### Scans & Tasks + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `SCAN_TIMEOUT` | `14400` | | Timeout for background scan/rescan tasks in seconds | +| `SCAN_WORKERS` | `1` | | Number of worker processes for scanning tasks | +| `TASK_TIMEOUT` | `300` | | Timeout for other background tasks in seconds | +| `TASK_RESULT_TTL` | `86400` | | How long to keep task results in Valkey in seconds | +| `SEVEN_ZIP_TIMEOUT` | `60` | | Timeout for 7-Zip operations in seconds | +| `ENABLE_RESCAN_ON_FILESYSTEM_CHANGE` | `false` | | Re-scan the library automatically when the filesystem changes | +| `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` | `5` | | Delay in minutes before re-scanning after a filesystem change | +| `ENABLE_SCHEDULED_RESCAN` | `false` | | Enable scheduled library re-scans | +| `SCHEDULED_RESCAN_CRON` | `0 3 * * *` | | Cron expression for scheduled re-scans | +| `ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB` | `false` | | Enable scheduled Switch TitleDB index updates | +| `SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON` | `0 4 * * *` | | Cron expression for scheduled Switch TitleDB updates | +| `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA` | `false` | | Enable scheduled LaunchBox metadata updates | +| `SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON` | `0 4 * * *` | | Cron expression for scheduled LaunchBox metadata updates | +| `ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP` | `false` | | Enable scheduled conversion of images to WebP | +| `SCHEDULED_CONVERT_IMAGES_TO_WEBP_CRON` | `0 4 * * *` | | Cron expression for scheduled WebP conversion | +| `ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC` | `false` | | Enable scheduled RetroAchievements progress sync | +| `SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC_CRON` | `0 4 * * *` | | Cron expression for scheduled RetroAchievements sync | + +### Sync + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `SYNC_BASE_PATH` | | | Base folder for sync state (defaults to $ROMM_BASE_PATH/sync) | +| `ENABLE_SYNC_FOLDER_WATCHER` | `false` | | Watch the sync folder and trigger scans on change | +| `SYNC_FOLDER_SCAN_DELAY` | `2` | | Delay in minutes before scanning after a sync folder change | +| `ENABLE_SYNC_PUSH_PULL` | `false` | | Enable scheduled sync push/pull | +| `SYNC_PUSH_PULL_CRON` | `*/30 * * * *` | | Cron expression for scheduled sync push/pull | +| `SYNC_SSH_KEYS_PATH` | | | Path to SSH keys for sync remotes (defaults to $ROMM_BASE_PATH/sync/keys) | +| `SYNC_SSH_KNOWN_HOSTS_PATH` | | | Path to SSH known_hosts (defaults to $ROMM_BASE_PATH/sync/known_hosts) | + +### Emulation + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `DISABLE_EMULATOR_JS` | `false` | | Disable in-browser play via EmulatorJS | +| `DISABLE_RUFFLE_RS` | `false` | | Disable in-browser Flash playback via RuffleRS | + +### Integrations + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `YOUTUBE_BASE_URL` | `https://www.youtube.com` | | Base URL for alternate YouTube frontends (Piped, Invidious, etc.) | +| `TINFOIL_WELCOME_MESSAGE` | `RomM Switch Library` | | Welcome message shown in Tinfoil Switch clients | + +### Logging + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `LOGLEVEL` | `INFO` | | Application log level | +| `FORCE_COLOR` | `false` | | Force colored log output | +| `NO_COLOR` | `false` | | Disable colored log output | + +### Web Server + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `WEB_SERVER_CONCURRENCY` | `1` | | Number of worker processes (recommended: 2 × CPU cores + 1) | +| `WEB_SERVER_TIMEOUT` | `300` | | Timeout for web server requests in seconds | +| `WEB_SERVER_KEEPALIVE` | `2` | | Keep-Alive connection wait time in seconds | +| `WEB_SERVER_MAX_REQUESTS` | `1000` | | Maximum requests a worker processes before restarting | +| `WEB_SERVER_MAX_REQUESTS_JITTER` | `100` | | Random jitter added to max requests value | +| `WEB_SERVER_WORKER_CONNECTIONS` | `1000` | | Maximum simultaneous clients per worker process | +| `WEB_SERVER_GUNICORN_WAIT_SECONDS` | `30` | | Seconds to wait for Gunicorn to start before giving up | +| `IPV4_ONLY` | `false` | | Bind only to IPv4 | + +### Proxy + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `HTTP_PROXY` | | | HTTP proxy URL for outbound requests | +| `HTTPS_PROXY` | | | HTTPS proxy URL for outbound requests | +| `NO_PROXY` | | | Comma-separated list of hosts to bypass the proxy | + +### Observability + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `SENTRY_DSN` | | | DSN for Sentry error tracking | + +### Development + +| Variable | Default | Required | Description | +| --- | --- | :---: | --- | +| `DEV_MODE` | `false` | | Enable development mode (debugging, hot-reloading) | +| `DEV_HOST` | `127.0.0.1` | | Host for the development server | +| `DEV_PORT` | `5000` | | Port for the development server | +| `DEV_HTTPS` | `false` | | Enable HTTPS in the development server | +| `DEV_SQL_ECHO` | `false` | | Log all SQL queries in development mode | +| `POSTGRES_DB` | `authentik` | | Postgres database name for the Authentik dev stack | +| `POSTGRES_USER` | `authentik` | | Postgres user for the Authentik dev stack | +| `POSTGRES_PASSWORD` | `authentik` | | Postgres password for the Authentik dev stack | +| `AUTHENTIK_SECRET_KEY` | | | Authentik secret key | +| `AUTHENTIK_BOOTSTRAP_PASSWORD` | | | Initial Authentik admin bootstrap password | diff --git a/docs/scripts/gen_env_vars.py b/docs/scripts/gen_env_vars.py index 15a537f6..a107f636 100644 --- a/docs/scripts/gen_env_vars.py +++ b/docs/scripts/gen_env_vars.py @@ -112,7 +112,7 @@ def render(rows: Iterable[dict]) -> str: out.append("| --- | --- | :---: | --- |") for r in items: default = f"`{r['default']}`" if r["default"] else "" - required = "✓" if r["required"] else "" + required = "`✓`" if r["required"] else "" desc = r["description"] or "—" out.append( f"| `{r['name']}` | {default} | {required} | {desc} |" From e5a697df87a4638009f74b6dedd75223e98f2180 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 16:36:33 -0400 Subject: [PATCH 027/121] tweak env vars page --- docs/reference/environment-variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 936c833e..4699dbd4 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -38,7 +38,7 @@ services: Don't embed `ROMM_AUTH_SECRET_KEY`, DB passwords, or provider API keys directly in a committed compose file. Use: - A `.env` that's `.gitignore`d. -- Docker secrets (`ROMM_AUTH_SECRET_KEY_FILE` reads from a mounted file). +- Docker secrets (`ROMM_AUTH_SECRET_KEY_FILE` reads from a [mounted file](https://docs.docker.com/compose/how-to/use-secrets/)). - Your orchestrator's secret store (K8s Secrets, HashiCorp Vault, AWS Secrets Manager). ## Essential variables From 54deb86b8201e3d528fb8c19833b726aba569d52 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 18:02:12 -0400 Subject: [PATCH 028/121] replace em dashes with conventional punctuation across docs Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/about/brand-guidelines.md | 6 +- docs/about/credits.md | 80 ++++----- docs/about/faqs.md | 22 +-- docs/about/license.md | 20 +-- docs/administration/administration-page.md | 52 +++--- docs/administration/authentication.md | 32 ++-- docs/administration/firmware-management.md | 34 ++-- docs/administration/index.md | 36 ++-- .../invitations-and-registration.md | 22 +-- docs/administration/metadata-providers.md | 24 +-- docs/administration/observability.md | 24 +-- docs/administration/oidc/authelia.md | 6 +- docs/administration/oidc/authentik.md | 10 +- docs/administration/oidc/index.md | 32 ++-- docs/administration/oidc/keycloak.md | 10 +- docs/administration/oidc/pocketid.md | 10 +- docs/administration/oidc/zitadel.md | 18 +- docs/administration/scanning-and-watcher.md | 22 +-- docs/administration/scheduled-tasks.md | 32 ++-- docs/administration/server-stats.md | 20 +-- docs/administration/ssh-sync.md | 22 +-- docs/administration/users-and-roles.md | 24 +-- docs/developers/api-authentication.md | 20 +-- docs/developers/api-reference.md | 164 +++++++++--------- docs/developers/architecture.md | 58 +++---- docs/developers/contributing.md | 26 +-- docs/developers/development-setup.md | 28 +-- docs/developers/i18n.md | 26 +-- docs/developers/index.md | 40 ++--- docs/developers/openapi.md | 28 +-- docs/developers/releasing.md | 40 ++--- docs/developers/websockets.md | 39 +++-- docs/ecosystem/argosy.md | 32 ++-- docs/ecosystem/client-api-tokens.md | 28 +-- docs/ecosystem/community-apps.md | 14 +- docs/ecosystem/device-sync-protocol.md | 34 ++-- docs/ecosystem/fpkgi.md | 28 +-- docs/ecosystem/grout.md | 52 +++--- docs/ecosystem/igir.md | 38 ++-- docs/ecosystem/index.md | 32 ++-- docs/ecosystem/kekatsu.md | 18 +- docs/ecosystem/muos-app.md | 36 ++-- docs/ecosystem/pkgj.md | 24 +-- docs/ecosystem/playnite-plugin.md | 28 +-- docs/ecosystem/tinfoil.md | 26 +-- docs/ecosystem/webrcade.md | 24 +-- docs/getting-started/concepts.md | 26 +-- docs/getting-started/first-scan.md | 36 ++-- docs/getting-started/folder-structure.md | 22 +-- docs/getting-started/quick-start.md | 10 +- docs/getting-started/what-is-new-in-5.md | 24 +-- docs/index.md | 4 +- docs/install/backup-and-restore.md | 20 +-- docs/install/databases.md | 10 +- docs/install/docker-compose.md | 22 +-- docs/install/image-variants.md | 12 +- docs/install/index.md | 38 ++-- docs/install/kubernetes.md | 16 +- docs/install/redis-or-valkey.md | 18 +- docs/install/reverse-proxy.md | 8 +- docs/install/synology.md | 18 +- docs/install/truenas.md | 20 +-- docs/install/unraid.md | 26 +-- docs/platforms/custom-platforms.md | 12 +- docs/platforms/emulatorjs-config.md | 30 ++-- docs/platforms/firmware-by-platform.md | 34 ++-- docs/platforms/index.md | 20 +-- docs/platforms/ms-dos.md | 64 +++---- docs/platforms/ruffle-config.md | 40 ++--- docs/platforms/supported-platforms.md | 24 +-- docs/reference/configuration-file.md | 28 +-- docs/reference/environment-variables.md | 14 +- docs/reference/exports.md | 14 +- docs/reference/feeds.md | 18 +- docs/reference/glossary.md | 126 +++++++------- docs/reference/ports-and-endpoints.md | 12 +- docs/reference/scheduled-tasks.md | 26 +-- docs/releases/changelog.md | 26 +-- docs/releases/index.md | 32 ++-- docs/releases/upgrading-to-3.0.md | 18 +- docs/releases/upgrading-to-5.0.md | 64 +++---- docs/resources/snippets/env-vars.md | 2 +- docs/resources/snippets/scheduled-tasks.md | 10 +- .../resources/snippets/supported-platforms.md | 2 +- docs/scripts/gen_env_vars.py | 8 +- docs/scripts/gen_platforms.py | 2 +- docs/scripts/gen_scheduled_tasks.py | 16 +- docs/troubleshooting/authentication.md | 18 +- docs/troubleshooting/in-browser-play.md | 34 ++-- docs/troubleshooting/index.md | 12 +- docs/troubleshooting/kubernetes.md | 8 +- docs/troubleshooting/miscellaneous.md | 18 +- docs/troubleshooting/netplay.md | 26 +-- docs/troubleshooting/scanning.md | 22 +-- docs/troubleshooting/sync.md | 32 ++-- docs/troubleshooting/synology.md | 14 +- docs/using/account-and-profile.md | 43 ++--- docs/using/collections.md | 30 ++-- docs/using/console-mode.md | 39 +++-- docs/using/downloads.md | 32 ++-- docs/using/in-browser-play.md | 50 +++--- docs/using/index.md | 38 ++-- docs/using/languages.md | 36 ++-- docs/using/library.md | 106 +++++------ docs/using/mobile-and-tv.md | 45 ++--- docs/using/netplay.md | 34 ++-- docs/using/pwa.md | 46 ++--- docs/using/retroachievements.md | 36 ++-- docs/using/rom-patcher.md | 44 ++--- docs/using/saves-and-states.md | 38 ++-- docs/using/smart-collections.md | 78 ++++----- docs/using/uploads.md | 34 ++-- docs/using/virtual-collections.md | 34 ++-- 113 files changed, 1682 insertions(+), 1678 deletions(-) diff --git a/docs/about/brand-guidelines.md b/docs/about/brand-guidelines.md index 80a5ae5e..494e9d41 100644 --- a/docs/about/brand-guidelines.md +++ b/docs/about/brand-guidelines.md @@ -75,9 +75,9 @@ If you're building something that integrates with RomM and would like to use / r The logo assets live at [rommapp/romm/tree/master/frontend/assets/](https://github.com/rommapp/romm/tree/master/frontend/assets/): -- `isotipo.svg` / `isotipo.png` — the mark (circular logo). -- `logotipo.svg` / `logotipo.png` — the wordmark. -- `social_preview.png` — GitHub social preview. +- `isotipo.svg` / `isotipo.png`: the mark (circular logo). +- `logotipo.svg` / `logotipo.png`: the wordmark. +- `social_preview.png`: GitHub social preview. ## Questions diff --git a/docs/about/credits.md b/docs/about/credits.md index 298df4a9..76ef3b59 100644 --- a/docs/about/credits.md +++ b/docs/about/credits.md @@ -5,11 +5,11 @@ description: The humans, projects, and services that make RomM possible. # Credits -RomM exists because a lot of people contributed code, designs, translations, ideas, and — most importantly — running bug reports back to the project. Thanks to every one of them. +RomM exists because a lot of people contributed code, designs, translations, ideas, and, most importantly, running bug reports back to the project. Thanks to every one of them. ## Core maintainers -The small team that reviews PRs, cuts releases, and keeps the project moving. Check [rommapp/romm](https://github.com/rommapp/romm/graphs/contributors) for the current active list — credits here would drift. +The small team that reviews PRs, cuts releases, and keeps the project moving. Check [rommapp/romm](https://github.com/rommapp/romm/graphs/contributors) for the current active list; credits here would drift. ## Contributors @@ -35,57 +35,57 @@ RomM stands on an enormous amount of open-source work. In rough order of "how vi ### In-browser emulation -- [EmulatorJS](https://emulatorjs.org/) — the retro emulator that powers most of our in-browser play. -- [Ruffle](https://ruffle.rs/) — the Flash / Shockwave emulator. -- [dosbox-pure](https://github.com/schellingb/dosbox-pure) — DOS emulation core via EmulatorJS. +- [EmulatorJS](https://emulatorjs.org/): the retro emulator that powers most of our in-browser play. +- [Ruffle](https://ruffle.rs/): the Flash / Shockwave emulator. +- [dosbox-pure](https://github.com/schellingb/dosbox-pure): DOS emulation core via EmulatorJS. ### Metadata sources -- [IGDB](https://www.igdb.com/) — the Internet Game Database. -- [ScreenScraper](https://screenscraper.fr/) — French community metadata. -- [MobyGames](https://www.mobygames.com/) — game database. -- [RetroAchievements](https://retroachievements.org/) — achievements and hash matching. -- [SteamGridDB](https://www.steamgriddb.com/) — cover art. -- [Hasheous](https://hasheous.org/) — hash-based matching. -- [PlayMatch](https://github.com/RetroRealm/playmatch) — community hash service. -- [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) — local metadata DB. -- [TheGamesDB](https://thegamesdb.net/) — free community DB. -- [Flashpoint Archive](https://flashpointproject.github.io/flashpoint-database/) — Flash game preservation. -- [HowLongToBeat](https://howlongtobeat.com/) — completion times. -- [Libretro](https://www.libretro.com/) — core metadata. +- [IGDB](https://www.igdb.com/): the Internet Game Database. +- [ScreenScraper](https://screenscraper.fr/): French community metadata. +- [MobyGames](https://www.mobygames.com/): game database. +- [RetroAchievements](https://retroachievements.org/): achievements and hash matching. +- [SteamGridDB](https://www.steamgriddb.com/): cover art. +- [Hasheous](https://hasheous.org/): hash-based matching. +- [PlayMatch](https://github.com/RetroRealm/playmatch): community hash service. +- [LaunchBox Games Database](https://gamesdb.launchbox-app.com/): local metadata DB. +- [TheGamesDB](https://thegamesdb.net/): free community DB. +- [Flashpoint Archive](https://flashpointproject.github.io/flashpoint-database/): Flash game preservation. +- [HowLongToBeat](https://howlongtobeat.com/): completion times. +- [Libretro](https://www.libretro.com/): core metadata. ### Backend stack -- [FastAPI](https://fastapi.tiangolo.com/) + [Starlette](https://www.starlette.io/) — the web framework. -- [SQLAlchemy](https://www.sqlalchemy.org/) + [Alembic](https://alembic.sqlalchemy.org/) — ORM and migrations. -- [RQ](https://python-rq.org/) — background job queue. -- [MariaDB](https://mariadb.org/) / [PostgreSQL](https://www.postgresql.org/) / [MySQL](https://www.mysql.com/) — database backends. -- [Redis](https://redis.io/) / [Valkey](https://valkey.io/) — cache and queue. -- [nginx](https://nginx.org/) with [`mod_zip`](https://github.com/evanmiller/mod_zip) — reverse proxy + streaming zip downloads. -- [uv](https://docs.astral.sh/uv/) — Python package manager. +- [FastAPI](https://fastapi.tiangolo.com/) + [Starlette](https://www.starlette.io/): the web framework. +- [SQLAlchemy](https://www.sqlalchemy.org/) + [Alembic](https://alembic.sqlalchemy.org/): ORM and migrations. +- [RQ](https://python-rq.org/): background job queue. +- [MariaDB](https://mariadb.org/) / [PostgreSQL](https://www.postgresql.org/) / [MySQL](https://www.mysql.com/): database backends. +- [Redis](https://redis.io/) / [Valkey](https://valkey.io/): cache and queue. +- [nginx](https://nginx.org/) with [`mod_zip`](https://github.com/evanmiller/mod_zip): reverse proxy + streaming zip downloads. +- [uv](https://docs.astral.sh/uv/): Python package manager. ### Frontend stack -- [Vue 3](https://vuejs.org/) — frontend framework. -- [Vuetify](https://vuetifyjs.com/) — component library. -- [Pinia](https://pinia.vuejs.org/) — state management. -- [Vite](https://vitejs.dev/) — build tool. -- [Socket.IO](https://socket.io/) — real-time communication. -- [vue-i18n](https://vue-i18n.intlify.dev/) — localisation. -- [rom-patcher-js](https://www.marcrobledo.com/RomPatcher.js/) — the ROM patcher library. -- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) — PWA support. +- [Vue 3](https://vuejs.org/): frontend framework. +- [Vuetify](https://vuetifyjs.com/): component library. +- [Pinia](https://pinia.vuejs.org/): state management. +- [Vite](https://vitejs.dev/): build tool. +- [Socket.IO](https://socket.io/): real-time communication. +- [vue-i18n](https://vue-i18n.intlify.dev/): localisation. +- [rom-patcher-js](https://www.marcrobledo.com/RomPatcher.js/): the ROM patcher library. +- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/): PWA support. ### Docs stack -- [MkDocs](https://www.mkdocs.org/) + [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) — what you're reading. -- [mike](https://github.com/jimporter/mike) — docs versioning. -- [asciinema](https://asciinema.org/) — terminal recordings. +- [MkDocs](https://www.mkdocs.org/) + [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/): what you're reading. +- [mike](https://github.com/jimporter/mike): docs versioning. +- [asciinema](https://asciinema.org/): terminal recordings. ## Community -- [#selfh.st](https://selfh.st/) — early visibility for the project. -- [Hacker News](https://news.ycombinator.com/) — the launch-day bump. -- [Aikido Security](https://www.aikido.dev/) — security audit. +- [#selfh.st](https://selfh.st/): early visibility for the project. +- [Hacker News](https://news.ycombinator.com/): the launch-day bump. +- [Aikido Security](https://www.aikido.dev/): security audit. - Everyone who submitted a bug report, pinged Discord, or pushed a typo fix. ## Financial supporters @@ -98,5 +98,5 @@ Open a PR against this page. Credit is cheap; we'd rather err on the side of nam ## See also -- [License](license.md) — the legal bit. -- [Contributing](../developers/contributing.md) — if you want to be on this list. +- [License](license.md): the legal bit. +- [Contributing](../developers/contributing.md): if you want to be on this list. diff --git a/docs/about/faqs.md b/docs/about/faqs.md index 10171682..463278de 100644 --- a/docs/about/faqs.md +++ b/docs/about/faqs.md @@ -21,7 +21,7 @@ Not a direct comparison page. Short version: RomM emphasises self-hosted + multi ## Do I need metadata API keys? -Not strictly. RomM runs without any — games just won't match to a metadata source, so no covers, descriptions, or ratings. +Not strictly. RomM runs without any; games just won't match to a metadata source, so no covers, descriptions, or ratings. Recommended: IGDB + ScreenScraper. See [Metadata Providers](../administration/metadata-providers.md) for the full list. @@ -51,7 +51,7 @@ Not supported: - **Comfortable** (thousands of ROMs, a few users, occasional scans): 2 GB RAM, 2 cores. - **In-browser play**: browser-side resource-heavy, RomM-server-side negligible. -Heaviest CPU is during scans — hashing + network-bound metadata calls. Plan for spikes. +Heaviest CPU is during scans: hashing + network-bound metadata calls. Plan for spikes. ## How do I update? @@ -70,19 +70,19 @@ Platform folder name probably doesn't match a known slug. Check [Supported Platf Most common reasons: -- No metadata providers configured — enable at least one. -- Filename too generic (no tags, unusual naming) — add filename tags like `(igdb-1234)` or try another provider. -- Wrong platform detection — see previous FAQ. +- No metadata providers configured: enable at least one. +- Filename too generic (no tags, unusual naming): add filename tags like `(igdb-1234)` or try another provider. +- Wrong platform detection: see previous FAQ. Full troubleshooting: [Scanning Troubleshooting](../troubleshooting/scanning.md). ## Can I share my library with friends? -Yes — add them as users via the invite flow, then either share the URL (if accessible to them) or put RomM behind a VPN / Tailscale. See [Invitations & Registration](../administration/invitations-and-registration.md) + [Mobile & TV → Self-hosting tips](../using/mobile-and-tv.md#self-hosting-tips). +Yes: add them as users via the invite flow, then either share the URL (if accessible to them) or put RomM behind a VPN / Tailscale. See [Invitations & Registration](../administration/invitations-and-registration.md) + [Mobile & TV → Self-hosting tips](../using/mobile-and-tv.md#self-hosting-tips). ## Can guests browse without an account? -Yes — set `KIOSK_MODE=true`. Anonymous visitors get read-only access. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). +Yes: set `KIOSK_MODE=true`. Anonymous visitors get read-only access. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). ## How do I back up? @@ -125,7 +125,7 @@ Open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) with: - RomM version. - Deployment (Docker Compose / Unraid / K8s / etc.). - Exact repro steps. -- Relevant logs — redact any secrets. +- Relevant logs: redact any secrets. ## Who runs RomM? @@ -141,6 +141,6 @@ A small team of maintainers plus a chunk of active community contributors. AGPL- ## See also -- [Core Concepts](../getting-started/concepts.md) — if the vocabulary is new. -- [Troubleshooting](../troubleshooting/index.md) — if something's broken. -- [Release Notes](../releases/index.md) — for version-specific questions. +- [Core Concepts](../getting-started/concepts.md): if the vocabulary is new. +- [Troubleshooting](../troubleshooting/index.md): if something's broken. +- [Release Notes](../releases/index.md): for version-specific questions. diff --git a/docs/about/license.md b/docs/about/license.md index 94eb6d91..38927e58 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -5,7 +5,7 @@ description: How RomM is licensed, and what that means for you. # License -## Core app — AGPL-3.0 +## Core app: AGPL-3.0 The main [RomM application](https://github.com/rommapp/romm) is licensed under the [GNU Affero General Public License v3.0](https://choosealicense.com/licenses/agpl-3.0/). @@ -16,13 +16,13 @@ In short, AGPL says: - **You must** make your modifications available under AGPL-3.0 if you distribute them *or* run them as a network-accessible service for others. - **You must** preserve copyright and license notices. -The "network service" clause is the key AGPL twist — if you host a modified RomM and other people use it over the network, you owe them the source. This prevents the "hosted SaaS fork without upstream contributions" failure mode that's common with plain GPL. +The "network service" clause is the key AGPL twist: if you host a modified RomM and other people use it over the network, you owe them the source. This prevents the "hosted SaaS fork without upstream contributions" failure mode that's common with plain GPL. -For most self-hosters this has zero practical effect — you're running an unmodified version for yourself, the license doesn't constrain you. For companies thinking about shipping a modified RomM commercially, understand the AGPL obligations first. +For most self-hosters this has zero practical effect: you're running an unmodified version for yourself, the license doesn't constrain you. For companies thinking about shipping a modified RomM commercially, understand the AGPL obligations first. Full license text: [rommapp/romm/blob/master/LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE). -## Companion apps — varies +## Companion apps: varies The RomM umbrella hosts several projects under different licenses: @@ -35,13 +35,13 @@ The RomM umbrella hosts several projects under different licenses: | [rommapp/muos-app](https://github.com/rommapp/muos-app) | check repo | | [rommapp/docs](https://github.com/rommapp/docs) (what you're reading) | CC0 | -Companion repos use more permissive licenses (GPL-3.0 or MIT) because they're smaller, more-replaceable, and don't host the library — the AGPL network-service clause doesn't offer the same protection benefits there. +Companion repos use more permissive licenses (GPL-3.0 or MIT) because they're smaller, more-replaceable, and don't host the library; the AGPL network-service clause doesn't offer the same protection benefits there. -Docs (this site) are CC0 — do whatever you want with the content; attribution appreciated but not required. +Docs (this site) are CC0: do whatever you want with the content; attribution appreciated but not required. ## Third-party components -RomM ships several third-party components with their own licenses — [EmulatorJS](https://emulatorjs.org/), [Ruffle](https://ruffle.rs/), Vue, FastAPI, and a long list of smaller dependencies. Their licenses apply to their respective code; none of them override AGPL-3.0 on the RomM code itself. +RomM ships several third-party components with their own licenses: [EmulatorJS](https://emulatorjs.org/), [Ruffle](https://ruffle.rs/), Vue, FastAPI, and a long list of smaller dependencies. Their licenses apply to their respective code; none of them override AGPL-3.0 on the RomM code itself. Full list via `uv tree` in the backend and `npm ls` in the frontend. Redistribution respects each upstream's terms. @@ -53,7 +53,7 @@ Yes. AGPL doesn't restrict private or commercial use. ### Can I fork RomM and relicense my fork? -No. AGPL is a strong copyleft — forks remain AGPL. You can add your own changes under AGPL, but can't relicense the original code. +No. AGPL is a strong copyleft; forks remain AGPL. You can add your own changes under AGPL, but can't relicense the original code. ### Can I charge money for RomM-as-a-service? @@ -75,6 +75,6 @@ Full contributor terms: [Contributing → Licensing](../developers/contributing. ## See also -- [Credits](credits.md) — the humans and projects behind RomM. -- [AGPL-3.0 overview](https://choosealicense.com/licenses/agpl-3.0/) — plain-language explainer. +- [Credits](credits.md): the humans and projects behind RomM. +- [AGPL-3.0 overview](https://choosealicense.com/licenses/agpl-3.0/): plain-language explainer. - Full license text: [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE). diff --git a/docs/administration/administration-page.md b/docs/administration/administration-page.md index 21f69a16..5284f452 100644 --- a/docs/administration/administration-page.md +++ b/docs/administration/administration-page.md @@ -1,13 +1,13 @@ --- title: Administration Page -description: A tour of the in-app Administration UI — where every operator control lives. +description: A tour of the in-app Administration UI, where every operator control lives. --- # Administration Page Click your **profile avatar** (top right, any page) to open the settings drawer. The links you see depend on your role. Admins see everything; Editors and Viewers see a subset. -This page is a map of what's behind each link. The deep mechanics of each feature live on their own pages — this is where to click. +This page is a map of what's behind each link. The deep mechanics of each feature live on their own pages; this is where to click. ## The drawer @@ -19,16 +19,16 @@ This page is a map of what's behind each link. The deep mechanics of each featur | **Metadata Sources** | Admins | Credentials for the 13 metadata providers; scan priority. | | **Administration** | Admins | Users, Client API Tokens, Tasks. The main admin hub. | | **Client API Tokens** | Everyone (own tokens) | Each user's personal API tokens. Admins see a separate "all tokens" view under Administration. | -| **Server Stats** | Admins | Numbers — platforms, games, saves, states, screenshots, disk usage. | +| **Server Stats** | Admins | Numbers: platforms, games, saves, states, screenshots, disk usage. | | **About** | Everyone | RomM version, links to Discord / GitHub / docs. | ## Profile The thing every user touches. -- **Username / email / password** — self-serve changes. Password changes require the current password. -- **Avatar** — upload a small image; displayed next to your name everywhere. -- **RetroAchievements** — set your RA username to link accounts; "Sync now" pulls fresh progression data. +- **Username / email / password**: self-serve changes. Password changes require the current password. +- **Avatar**: upload a small image; displayed next to your name everywhere. +- **RetroAchievements**: set your RA username to link accounts; "Sync now" pulls fresh progression data. See [Users & Roles](users-and-roles.md) for what role-specific self-serve is allowed. @@ -36,19 +36,19 @@ See [Users & Roles](users-and-roles.md) for what role-specific self-serve is all Per-user UI preferences. Stored in the user's row + localStorage, not in `config.yml`. -- **Language** — 19 locales supported; see [Languages](../using/languages.md) for the list. -- **Theme** — Dark, Light, or Auto (follows OS preference). Palette overrides via `extra_css` are operator-level. -- **Game card layout** — cover style (2D, 3D boxart, poster), info-density, the `vanilla-tilt` 3D hover effect on/off. -- **Home dashboard ribbons** — show/hide "Recently Added", "Continue Playing", "Collections", etc. -- **Virtual collections** — enable/disable auto-generated groupings (by genre, developer, year, etc.). +- **Language**: 19 locales supported; see [Languages](../using/languages.md) for the list. +- **Theme**: Dark, Light, or Auto (follows OS preference). Palette overrides via `extra_css` are operator-level. +- **Game card layout**: cover style (2D, 3D boxart, poster), info-density, the `vanilla-tilt` 3D hover effect on/off. +- **Home dashboard ribbons**: show/hide "Recently Added", "Continue Playing", "Collections", etc. +- **Virtual collections**: enable/disable auto-generated groupings (by genre, developer, year, etc.). ## Library Management Editor-grade tools for catalogue hygiene. -- **Platform bindings** — map a filesystem folder name (`super_nintendo/`) to a platform slug (`snes`). Mirrors `system.platforms` in `config.yml`. -- **Platform versions** — some platforms have multiple IGDB "versions" (e.g. Mega Drive vs Genesis). Pin which one RomM uses for lookups. -- **Missing ROMs** — a filter/table view showing DB entries whose files are gone. Bulk-delete from here, or run the [Cleanup Missing ROMs](scheduled-tasks.md#cleanup-missing-roms-manual) task. +- **Platform bindings**: map a filesystem folder name (`super_nintendo/`) to a platform slug (`snes`). Mirrors `system.platforms` in `config.yml`. +- **Platform versions**: some platforms have multiple IGDB "versions" (e.g. Mega Drive vs Genesis). Pin which one RomM uses for lookups. +- **Missing ROMs**: a filter/table view showing DB entries whose files are gone. Bulk-delete from here, or run the [Cleanup Missing ROMs](scheduled-tasks.md#cleanup-missing-roms-manual) task. ## Metadata Sources @@ -63,10 +63,10 @@ The main admin hub. Three sub-panels: ### Users - Table of all users with role, last login, creation date. -- **Add** — manual user creation with username + email + password + role. -- **Invite** — generate an invite link. See [Invitations & Registration](invitations-and-registration.md). -- **Edit** — change username, email, role, password. Reset password by typing a new one. -- **Delete** — red trash icon. RomM won't let you delete yourself or the last admin. +- **Add**: manual user creation with username + email + password + role. +- **Invite**: generate an invite link. See [Invitations & Registration](invitations-and-registration.md). +- **Edit**: change username, email, role, password. Reset password by typing a new one. +- **Delete**: red trash icon. RomM won't let you delete yourself or the last admin. ### Client API Tokens @@ -76,7 +76,7 @@ The main admin hub. Three sub-panels: ### Tasks -- Status of every scheduled / manual / watcher task — queued, running, idle, failed. +- Status of every scheduled / manual / watcher task: queued, running, idle, failed. - **Run** button per task (requires `tasks.run` scope). - See [Scheduled Tasks](scheduled-tasks.md) for what each one does. @@ -86,7 +86,7 @@ Admin-only. A dashboard of counts and sizes: - Total platforms, games, saves, states, screenshots. - Total disk footprint (library + resources + assets). -- Per-platform breakdown — handy for spotting a platform that's ballooned. +- Per-platform breakdown: handy for spotting a platform that's ballooned. Full details in [Server Stats](server-stats.md). @@ -111,9 +111,9 @@ Full shortcut reference is on the [Using RomM](../using/index.md) page. | User Interface | ✓ | ✓ | ✓ | | Client API Tokens (own) | ✓ | ✓ | ✓ | | About | ✓ | ✓ | ✓ | -| Library Management | — | ✓ | ✓ | -| Metadata Sources | — | — | ✓ | -| Administration → Users | — | — | ✓ | -| Administration → Tokens (all) | — | — | ✓ | -| Administration → Tasks | — | — | ✓ | -| Server Stats | — | — | ✓ | +| Library Management | - | ✓ | ✓ | +| Metadata Sources | - | - | ✓ | +| Administration → Users | - | - | ✓ | +| Administration → Tokens (all) | - | - | ✓ | +| Administration → Tasks | - | - | ✓ | +| Server Stats | - | - | ✓ | diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index 09e2271b..8291c084 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -1,19 +1,19 @@ --- title: Authentication -description: Configure how users sign in — sessions, password policy, client tokens, OIDC hooks, and kiosk mode. +description: Configure how users sign in: sessions, password policy, client tokens, OIDC hooks, and kiosk mode. --- # Authentication -This page is the **operator-side** authentication reference: knobs you turn on the server to control how people sign in. The **client-side** reference — "how do I actually authenticate an API call?" — is in [API Authentication](../developers/api-authentication.md). +This page is the **operator-side** authentication reference: knobs you turn on the server to control how people sign in. The **client-side** reference ("how do I actually authenticate an API call?") is in [API Authentication](../developers/api-authentication.md). Authentication flows RomM supports: -- **Username + password** (default) — local account, bcrypt-hashed, stored in the DB. -- **OIDC** — single sign-on via an external IdP. See [OIDC Setup](oidc/index.md). -- **Client API Tokens** — long-lived per-user tokens for companion apps and scripts. -- **Device pairing** — short codes for bootstrapping a token onto a handheld. Covered in [Client API Tokens](../ecosystem/client-api-tokens.md). -- **Kiosk mode** — unauthenticated read-only access. Toggle for public demos / shared terminals. +- **Username + password** (default): local account, bcrypt-hashed, stored in the DB. +- **OIDC**: single sign-on via an external IdP. See [OIDC Setup](oidc/index.md). +- **Client API Tokens**: long-lived per-user tokens for companion apps and scripts. +- **Device pairing**: short codes for bootstrapping a token onto a handheld. Covered in [Client API Tokens](../ecosystem/client-api-tokens.md). +- **Kiosk mode**: unauthenticated read-only access. Toggle for public demos / shared terminals. ## Session config @@ -21,8 +21,8 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: | Variable | Default | What it controls | | --- | --- | --- | -| `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually** — it invalidates every active session and every outstanding invite link. | -| `ROMM_AUTH_SECRET_KEY_FILE` | — | Alternative: read the secret from a file. Useful with Docker secrets. | +| `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually**; it invalidates every active session and every outstanding invite link. | +| `ROMM_AUTH_SECRET_KEY_FILE` | _unset_ | Alternative: read the secret from a file. Useful with Docker secrets. | | `SESSION_MAX_AGE_SECONDS` | 86400 (24 h) | How long a session cookie lives before the user has to sign in again. | | `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | 900 (15 min) | Short-lived OAuth2 access token TTL. | | `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | 2592000 (30 d) | Refresh token TTL for OAuth2. | @@ -30,7 +30,7 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: ## Local (username + password) -The default. Accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md). Passwords are bcrypt-hashed — RomM does not log or store plaintext passwords at any point. +The default. Accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md). Passwords are bcrypt-hashed; RomM does not log or store plaintext passwords at any point. **Disable local password login entirely** (force OIDC-only): @@ -48,11 +48,11 @@ Until email-based self-serve reset lands, admins set passwords manually: **Administration → Users → Edit → New password → Save.** -The next login on that account will use the new password; existing sessions for that user remain valid until they expire — revoke them explicitly by deleting all of the user's Client API Tokens if that's a concern. +The next login on that account will use the new password; existing sessions for that user remain valid until they expire; revoke them explicitly by deleting all of the user's Client API Tokens if that's a concern. ### Self-serve password reset -Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA — the UI path exists and will light up once email config is exposed.) +Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA; the UI path exists and will light up once email config is exposed.) ## OIDC @@ -72,7 +72,7 @@ When OIDC is configured, the login page grows an "OIDC" button. Set `OIDC_AUTOLO ## Client API Tokens -For anything long-lived — a companion app, a cron job, a script — use **Client API Tokens** instead of storing a password. +For anything long-lived (a companion app, a cron job, a script) use **Client API Tokens** instead of storing a password. Create from **Administration → Client API Tokens**. Each token: @@ -81,11 +81,11 @@ Create from **Administration → Client API Tokens**. Each token: - Has an optional expiry (no expiry = never expires until manually revoked). - Can be "paired" to a device via a short code (covered in [Client API Tokens](../ecosystem/client-api-tokens.md)). -Each user gets up to 25 active tokens. Revoke from the same page. The API side — "how do I send this thing in a request?" — lives in [API Authentication](../developers/api-authentication.md). +Each user gets up to 25 active tokens. Revoke from the same page. The API side ("how do I send this thing in a request?") lives in [API Authentication](../developers/api-authentication.md). ## Kiosk mode -Turns every GET endpoint into unauthenticated read-only access — anyone reaching the instance can browse, but nobody can write, scan, upload, or manage users. +Turns every GET endpoint into unauthenticated read-only access: anyone reaching the instance can browse, but nobody can write, scan, upload, or manage users. ```yaml environment: @@ -109,7 +109,7 @@ environment: Skips auth on `GET /api/roms/{id}/content/…` and the firmware download endpoint. Exists so third-party apps that can't carry a bearer header (dumb emulators loading a ROM by URL) can still pull files. -**Only enable this when the public internet can't reach RomM directly** — i.e. there's auth or an IP allowlist at the reverse-proxy layer. Otherwise you've just made your library world-downloadable. +**Only enable this when the public internet can't reach RomM directly**, i.e. there's auth or an IP allowlist at the reverse-proxy layer. Otherwise you've just made your library world-downloadable. ## Revoking access diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index e2c34492..118207b8 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -5,26 +5,26 @@ description: Upload, associate, and serve BIOS/firmware files for emulation. # Firmware Management -Many emulated platforms require BIOS or firmware to boot — PlayStation (SCPH1001), Saturn, Game Boy Advance, Sega CD, and more. RomM tracks firmware files **per platform**, stores them on disk, and serves them to in-browser players (EmulatorJS / Ruffle) and companion apps that request them. +Many emulated platforms require BIOS or firmware to boot: PlayStation (SCPH1001), Saturn, Game Boy Advance, Sega CD, and more. RomM tracks firmware files **per platform**, stores them on disk, and serves them to in-browser players (EmulatorJS / Ruffle) and companion apps that request them. Firmware is **not** ROM. Keep the two separate: -- `/romm/library/roms/` — games you own dumps of. -- `/romm/library/bios/` (Structure A) or `/romm/library/{platform}/bios/` (Structure B) — system firmware. +- `/romm/library/roms/`: games you own dumps of. +- `/romm/library/bios/` (Structure A) or `/romm/library/{platform}/bios/` (Structure B): system firmware. Legality varies by jurisdiction. RomM does not ship firmware and the project cannot help you obtain it. ## Two paths to ingest firmware -### Path 1 — drop it in the folder, let the scan pick it up +### Path 1: drop it in the folder, let the scan pick it up The usual workflow if you have firmware on disk already. -1. Put the file in the right `bios/` folder (Structure A or B — see [Folder Structure](../getting-started/folder-structure.md)). +1. Put the file in the right `bios/` folder (Structure A or B; see [Folder Structure](../getting-started/folder-structure.md)). 2. Run a scan. Firmware is picked up alongside ROMs. 3. It shows up in **Administration → Library Management → Firmware**. -### Path 2 — upload through the UI +### Path 2: upload through the UI When you don't have shell access or you're uploading from a different machine. @@ -33,11 +33,11 @@ When you don't have shell access or you're uploading from a different machine. 3. Drag and drop the file, or click to browse. 4. RomM puts it in the correct `bios/{platform}/` directory on disk. -Editors and Admins can upload firmware; Viewers can't (`firmware.write` scope required — see [Users & Roles](users-and-roles.md)). +Editors and Admins can upload firmware; Viewers can't (`firmware.write` scope required, see [Users & Roles](users-and-roles.md)). ## Platform-specific firmware -Every emulator has its own requirements — which files it needs, specific hashes, naming conventions. The [Supported Platforms](../platforms/supported-platforms.md) table flags which platforms need firmware for EmulatorJS playback; a dedicated [Firmware by Platform](../platforms/firmware-by-platform.md) page lists what's needed for the popular ones. +Every emulator has its own requirements: which files it needs, specific hashes, naming conventions. The [Supported Platforms](../platforms/supported-platforms.md) table flags which platforms need firmware for EmulatorJS playback; a dedicated [Firmware by Platform](../platforms/firmware-by-platform.md) page lists what's needed for the popular ones. Common examples: @@ -49,7 +49,7 @@ Common examples: | Saturn | `saturn_bios.bin`, `mpr-17933.bin` | `bios/saturn/` | | Nintendo DS | `firmware.bin`, `bios9.bin`, `bios7.bin` | `bios/nds/` | -File naming matters — emulators look for specific filenames. Double-check against the emulator core's documentation if something won't boot. +File naming matters: emulators look for specific filenames. Double-check against the emulator core's documentation if something won't boot. ## Listing and deleting firmware @@ -66,10 +66,10 @@ File naming matters — emulators look for specific filenames. Double-check agai Firmware has a standard REST surface under `/api/firmware/`: -- `GET /api/firmware` — list (optional `?platform_id=` filter). -- `POST /api/firmware` — upload. -- `GET /api/firmware/{id}/content/{filename}` — download. -- `POST /api/firmware/delete` — bulk delete. +- `GET /api/firmware`: list (optional `?platform_id=` filter). +- `POST /api/firmware`: upload. +- `GET /api/firmware/{id}/content/{filename}`: download. +- `POST /api/firmware/delete`: bulk delete. Requires `firmware.read` / `firmware.write` scopes. See the [API Reference](../developers/api-reference.md). @@ -78,15 +78,15 @@ Requires `firmware.read` / `firmware.write` scopes. See the [API Reference](../d When a user launches a platform that requires firmware in EmulatorJS: 1. The player checks RomM for a matching firmware file. -2. If present, it's served directly — no user action required. -3. If missing, the player surfaces an error — "firmware required" — and the admin needs to upload one. +2. If present, it's served directly; no user action required. +3. If missing, the player surfaces an error ("firmware required") and the admin needs to upload one. Configure which emulator settings to expose to users via `emulatorjs.settings` in [`config.yml`](../reference/configuration-file.md). Details on the player side in [In-Browser Play](../using/in-browser-play.md). ## Integration with companion apps -Handheld syncers (Grout, Argosy, DeckRommSync) can pull firmware alongside ROMs. They use the same Client API Token auth as for ROMs — scope the token to include `firmware.read`. See [Client API Tokens](../ecosystem/client-api-tokens.md). +Handheld syncers (Grout, Argosy, DeckRommSync) can pull firmware alongside ROMs. They use the same Client API Token auth as for ROMs; scope the token to include `firmware.read`. See [Client API Tokens](../ecosystem/client-api-tokens.md). ## Backups -Firmware is user-owned data — back it up. `/romm/library/bios/` is part of your library mount; if you're backing up the library volume, firmware is already covered. See [Backup & Restore](../install/backup-and-restore.md). +Firmware is user-owned data: back it up. `/romm/library/bios/` is part of your library mount; if you're backing up the library volume, firmware is already covered. See [Backup & Restore](../install/backup-and-restore.md). diff --git a/docs/administration/index.md b/docs/administration/index.md index 38796e71..631913ee 100644 --- a/docs/administration/index.md +++ b/docs/administration/index.md @@ -1,45 +1,45 @@ --- title: Administration -description: Running RomM for yourself and others — users, auth, metadata, scans, tasks, backups. +description: Running RomM for yourself and others: users, auth, metadata, scans, tasks, backups. --- # Administration Administration is everything you do **as the operator** of a RomM instance: managing accounts, controlling access, configuring metadata sources, scheduling scans, watching the library for changes, monitoring the server, and keeping data safe. -The end-user equivalent — how to actually play the games, build collections, upload saves — lives in [Using RomM](../using/index.md). +The end-user equivalent (how to actually play the games, build collections, upload saves) lives in [Using RomM](../using/index.md). ## Where things live ### Users & access -- **[Users & Roles](users-and-roles.md)** — roles, the 19-scope model, how permissions add up. -- **[Invitations & Registration](invitations-and-registration.md)** — invite links, public signup, first-user setup. -- **[Authentication](authentication.md)** — session config, password reset, Client API Tokens for devices. -- **[OIDC Setup](oidc/index.md)** — Authelia, Authentik, Keycloak, PocketID, Zitadel — SSO + role mapping. +- **[Users & Roles](users-and-roles.md)**: roles, the 19-scope model, how permissions add up. +- **[Invitations & Registration](invitations-and-registration.md)**: invite links, public signup, first-user setup. +- **[Authentication](authentication.md)**: session config, password reset, Client API Tokens for devices. +- **[OIDC Setup](oidc/index.md)**: Authelia, Authentik, Keycloak, PocketID, Zitadel, SSO + role mapping. ### Content & library -- **[Metadata Providers](metadata-providers.md)** — all 13 providers, credentials, priority ordering. -- **[Scanning & Watcher](scanning-and-watcher.md)** — how scans work, scan modes, filesystem watcher. -- **[Firmware Management](firmware-management.md)** — BIOS/firmware uploads for emulation. +- **[Metadata Providers](metadata-providers.md)**: all 13 providers, credentials, priority ordering. +- **[Scanning & Watcher](scanning-and-watcher.md)**: how scans work, scan modes, filesystem watcher. +- **[Firmware Management](firmware-management.md)**: BIOS/firmware uploads for emulation. ### Operations -- **[Scheduled Tasks](scheduled-tasks.md)** — what runs in the background and how to tune it. -- **[Server Stats](server-stats.md)** — the stats page and what its numbers mean. -- **[Observability](observability.md)** — logs, Sentry, OpenTelemetry, `/heartbeat`. -- **[SSH Sync](ssh-sync.md)** — push/pull sync to handhelds and other devices. -- **[Administration Page](administration-page.md)** — the in-app admin UI tour. +- **[Scheduled Tasks](scheduled-tasks.md)**: what runs in the background and how to tune it. +- **[Server Stats](server-stats.md)**: the stats page and what its numbers mean. +- **[Observability](observability.md)**: logs, Sentry, OpenTelemetry, `/heartbeat`. +- **[SSH Sync](ssh-sync.md)**: push/pull sync to handhelds and other devices. +- **[Administration Page](administration-page.md)**: the in-app admin UI tour. ### Configuration -- **[Environment Variables](../reference/environment-variables.md)** — every env var, grouped by area. -- **[Configuration File](../reference/configuration-file.md)** — the `config.yml` schema. +- **[Environment Variables](../reference/environment-variables.md)**: every env var, grouped by area. +- **[Configuration File](../reference/configuration-file.md)**: the `config.yml` schema. ### Keeping data safe -- **[Backup & Restore](../install/backup-and-restore.md)** — routine backups, restore drill, host migration. +- **[Backup & Restore](../install/backup-and-restore.md)**: routine backups, restore drill, host migration. ## The role model in thirty seconds @@ -48,7 +48,7 @@ Three built-in roles, all backed by the same scope system: | Role | Summary | | --- | --- | | **Admin** | Full control. User management, task execution, every scope. First user always gets this. | -| **Editor** | Curate the library — edit ROMs, platforms, collections, upload firmware. No user management. | +| **Editor** | Curate the library: edit ROMs, platforms, collections, upload firmware. No user management. | | **Viewer** | Play games, manage own saves/states/profile. Read-only everywhere else. | Full scope matrix in [Users & Roles](users-and-roles.md). diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md index fedd27b2..5d8c834c 100644 --- a/docs/administration/invitations-and-registration.md +++ b/docs/administration/invitations-and-registration.md @@ -7,10 +7,10 @@ description: Inviting users, public signup, the first-user flow, and role assign Three ways a new account ends up on a RomM instance: -1. **First-user setup** — the person who completes the Setup Wizard. -2. **Invite link** — an admin generates a one-shot link carrying a pre-assigned role. -3. **Public registration** — anyone who reaches `/register` signs themselves up as a Viewer. Off by default. -4. **OIDC auto-provisioning** — first login through your IdP creates a matching account. Covered in [OIDC Setup](oidc/index.md). +1. **First-user setup**: the person who completes the Setup Wizard. +2. **Invite link**: an admin generates a one-shot link carrying a pre-assigned role. +3. **Public registration**: anyone who reaches `/register` signs themselves up as a Viewer. Off by default. +4. **OIDC auto-provisioning**: first login through your IdP creates a matching account. Covered in [OIDC Setup](oidc/index.md). ## First-user setup @@ -23,14 +23,14 @@ environment: - DISABLE_SETUP_WIZARD=true ``` -You'll then need to create the first admin via the API or by injecting a row at deploy time — the UI won't offer a setup flow. +You'll then need to create the first admin via the API or by injecting a row at deploy time; the UI won't offer a setup flow. ## Invite links The recommended way to add users, because it avoids you ever touching their password. 1. **Administration → Users → Invite.** Pick a role (Viewer, Editor, Admin). -2. RomM generates a single-use URL — copy it and send it to the invitee. +2. RomM generates a single-use URL; copy it and send it to the invitee. 3. When they open it, they pick their own username and password. RomM creates the account with the role you chose and logs them straight in. Invite tokens are **single-use** and **time-limited**. Defaults: @@ -39,10 +39,10 @@ Invite tokens are **single-use** and **time-limited**. Defaults: | --- | --- | --- | | Expiry | 30 days | `INVITE_TOKEN_DAYS` | -Expired links return a clear error on the `/register` page — generate a new one from the Users panel. +Expired links return a clear error on the `/register` page; generate a new one from the Users panel. !!! tip "Invitations over HTTPS" - Invite URLs include a signed token; they're not useful to anyone without RomM's `ROMM_AUTH_SECRET_KEY`. Still, send them over a trusted channel — once someone has a valid invite URL, they can claim the account. + Invite URLs include a signed token; they're not useful to anyone without RomM's `ROMM_AUTH_SECRET_KEY`. Still, send them over a trusted channel; once someone has a valid invite URL, they can claim the account. ## Public self-registration @@ -60,7 +60,7 @@ Appropriate for: - Instances behind auth at the reverse-proxy layer (Authelia, Cloudflare Access, an IP allowlist). RomM's registration is just paperwork once the proxy has already authenticated the visitor. - Truly public or group-shared instances where you genuinely want open signup. -Inappropriate for everything else. **If RomM is exposed to the internet with no upstream auth, leave this off** — it's the single fastest way to fill your DB with spam accounts. +Inappropriate for everything else. **If RomM is exposed to the internet with no upstream auth, leave this off**: it's the single fastest way to fill your DB with spam accounts. Anyone who signs up this way is a Viewer. Promote them manually from **Administration → Users → Edit** if needed. @@ -73,10 +73,10 @@ Anyone who signs up this way is a Viewer. Promote them manually from **Administr | Public registration (`ALLOW_PUBLIC_REGISTRATION=true`) | Viewer | | OIDC first login | Default Viewer, or mapped from claims via `OIDC_CLAIM_ROLES` | -Changing a user's role afterwards is a normal admin action — see [Users & Roles](users-and-roles.md). +Changing a user's role afterwards is a normal admin action; see [Users & Roles](users-and-roles.md). ## Password reset -If you've configured an email transport (future work — not part of 5.0 GA), users can self-serve a password reset via **Forgot password?** on the login page. Until then, admins reset passwords manually: **Administration → Users → Edit → New password**. +If you've configured an email transport (future work, not part of 5.0 GA), users can self-serve a password reset via **Forgot password?** on the login page. Until then, admins reset passwords manually: **Administration → Users → Edit → New password**. See [Authentication](authentication.md) for the session and token side of the auth model. diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md index 6c9dbe04..f6f2ae00 100644 --- a/docs/administration/metadata-providers.md +++ b/docs/administration/metadata-providers.md @@ -1,13 +1,13 @@ --- title: Metadata Providers -description: Configure the thirteen metadata sources RomM supports — IGDB, ScreenScraper, MobyGames, RetroAchievements, SteamGridDB, Hasheous, PlayMatch, LaunchBox, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro. +description: Configure the thirteen metadata sources RomM supports: IGDB, ScreenScraper, MobyGames, RetroAchievements, SteamGridDB, Hasheous, PlayMatch, LaunchBox, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro. --- # Metadata Providers -RomM pulls game metadata — titles, descriptions, cover art, screenshots, manuals, achievement data, completion times — from up to **thirteen** providers. You don't need all of them. This page covers the recommended combinations and per-provider setup. +RomM pulls game metadata (titles, descriptions, cover art, screenshots, manuals, achievement data, completion times) from up to **thirteen** providers. You don't need all of them. This page covers the recommended combinations and per-provider setup. -Configure providers either via env vars (below) or interactively in **Administration → Metadata Sources** in the UI. Scan priority (which provider wins when two disagree) is set in [`config.yml`](../reference/configuration-file.md) — see `scan.priority.metadata` and `scan.priority.artwork`. +Configure providers either via env vars (below) or interactively in **Administration → Metadata Sources** in the UI. Scan priority (which provider wins when two disagree) is set in [`config.yml`](../reference/configuration-file.md); see `scan.priority.metadata` and `scan.priority.artwork`. ## Popular combos @@ -51,7 +51,7 @@ Configure providers either via env vars (below) or interactively in **Administra Access requires a Twitch account and a phone number for 2FA. Up-to-date instructions live in the [IGDB API docs](https://api-docs.igdb.com/#account-creation). When registering your application in the Twitch Developer Portal: -- **Name**: something unique — picking an existing name fails silently. Use `romm-`. +- **Name**: something unique; picking an existing name fails silently. Use `romm-`. - **OAuth Redirect URLs**: `localhost` - **Category**: Application Integration - **Client Type**: Confidential @@ -77,7 +77,7 @@ Metadata, cover art, and screenshots. [Create an account](https://www.mobygames. ### SteamGridDB -[SteamGridDB](https://www.steamgriddb.com/) serves custom cover art for games and collections. It's not used by the scanner directly — it surfaces in the **Search Cover** button when you manually edit a game's artwork. +[SteamGridDB](https://www.steamgriddb.com/) serves custom cover art for games and collections. It's not used by the scanner directly; it surfaces in the **Search Cover** button when you manually edit a game's artwork. Log in with a [Steam account](https://store.steampowered.com/join), go to your [API tab](https://www.steamgriddb.com/profile/preferences/api), and set `STEAMGRIDDB_API_KEY`. @@ -85,7 +85,7 @@ Log in with a [Steam account](https://store.steampowered.com/join), go to your [ [RetroAchievements](https://retroachievements.org/) provides achievement data and hash matching. Generate a web API key from your RA [settings page](https://retroachievements.org/settings) and set `RETROACHIEVEMENTS_API_KEY`. Run an **Unmatched** scan on the platforms you want matched. -Each RomM user also links their own RA username in their profile to sync personal progression — a new **Achievements** tab appears on the **Personal** data panel once linked. +Each RomM user also links their own RA username in their profile to sync personal progression; a new **Achievements** tab appears on the **Personal** data panel once linked. The RA database is cached locally; refresh frequency is controlled by `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` (default: 30). @@ -103,7 +103,7 @@ The RA database is cached locally; refresh frequency is controlled by `REFRESH_R ### LaunchBox -The [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) is a community-driven catalogue. RomM downloads the full database locally and matches on exact filenames — just like the LaunchBox desktop app. +The [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) is a community-driven catalogue. RomM downloads the full database locally and matches on exact filenames, just like the LaunchBox desktop app. ```yaml environment: @@ -112,7 +112,7 @@ environment: - SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON=0 5 * * * # default: 5am daily ``` -Run at least one LaunchBox update (manually from the Scan page, or wait for the cron) before using it as a scan source — RomM won't match against an empty local DB. +Run at least one LaunchBox update (manually from the Scan page, or wait for the cron) before using it as a scan source; RomM won't match against an empty local DB. ### TheGamesDB @@ -120,7 +120,7 @@ Run at least one LaunchBox update (manually from the Scan page, or wait for the ### Flashpoint -The [Flashpoint Project Database](https://flashpointproject.github.io/flashpoint-database/) covers 180,000+ Flash and browser-based games — the thing Ruffle is for. Flag with `FLASHPOINT_API_ENABLED=true`. Run an **Unmatched** scan to update existing platforms. +The [Flashpoint Project Database](https://flashpointproject.github.io/flashpoint-database/) covers 180,000+ Flash and browser-based games, the thing Ruffle is for. Flag with `FLASHPOINT_API_ENABLED=true`. Run an **Unmatched** scan to update existing platforms. ### HowLongToBeat @@ -163,7 +163,7 @@ Two edits in the ES-DE settings file so ES-DE writes its metadata and media into ### Libretro -Libretro's retro core metadata is used internally for platform mapping and fallback artwork — no env flag, no credentials. Nothing to configure; RomM uses it automatically when it knows the libretro core for a platform. +Libretro's retro core metadata is used internally for platform mapping and fallback artwork: no env flag, no credentials. Nothing to configure; RomM uses it automatically when it knows the libretro core for a platform. ## Metadata tags in filenames @@ -178,7 +178,7 @@ RomM honours inline tags in ROM filenames to force a match against a specific pr | `(launchbox-xxxx)` | [LaunchBox](https://gamesdb.launchbox-app.com/) | | `(hltb-xxxx)` | [HowLongToBeat](https://howlongtobeat.com/) | -RomM will **not** rename your files to add these — they're opt-in, and renaming would conflict with other tooling that walks the filesystem. +RomM will **not** rename your files to add these; they're opt-in, and renaming would conflict with other tooling that walks the filesystem. ## Priority and conflict resolution @@ -210,6 +210,6 @@ scan: - hltb ``` -Reorder these lists to taste — for example, put `ss` first if you prefer ScreenScraper boxart, or move `hltb` up if you care about completion times more than descriptions. +Reorder these lists to taste. For example, put `ss` first if you prefer ScreenScraper boxart, or move `hltb` up if you care about completion times more than descriptions. See the full [Configuration File reference](../reference/configuration-file.md) for everything `scan.priority` can do. diff --git a/docs/administration/observability.md b/docs/administration/observability.md index 097a5964..a9af817f 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -7,10 +7,10 @@ description: Logs, Sentry error tracking, OpenTelemetry, and the /heartbeat endp Four ways to know what RomM is doing: -- **Container logs** — always available, the first stop. -- **`/api/heartbeat`** — health + config summary for uptime monitors. -- **Sentry** — opt-in error tracking with stack traces. -- **OpenTelemetry** — opt-in distributed tracing + metrics. +- **Container logs**: always available, the first stop. +- **`/api/heartbeat`**: health + config summary for uptime monitors. +- **Sentry**: opt-in error tracking with stack traces. +- **OpenTelemetry**: opt-in distributed tracing + metrics. ## Logs @@ -23,7 +23,7 @@ environment: - NO_COLOR=1 # 1 to disable colour entirely ``` -`INFO` is the default and sane for production. Drop to `DEBUG` only while debugging a specific issue — RomM is chatty on DEBUG. +`INFO` is the default and sane for production. Drop to `DEBUG` only while debugging a specific issue; RomM is chatty on DEBUG. ### Reading logs @@ -63,7 +63,7 @@ Returns: - Scheduled-task schedule summary. - Watcher status. -Wire this to your uptime monitor. A failure here is real — the process is down or the DB/Redis is unreachable. +Wire this to your uptime monitor. A failure here is real: the process is down or the DB/Redis is unreachable. ```bash # Basic uptime check @@ -116,14 +116,14 @@ environment: Standard [OTEL env vars](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) apply. RomM emits: -- **Traces** — HTTP request spans, DB query spans, RQ job spans. -- **Metrics** — request counts, durations, queue depth, scan progress. -- **Logs** — structured log correlation with trace IDs. +- **Traces**: HTTP request spans, DB query spans, RQ job spans. +- **Metrics**: request counts, durations, queue depth, scan progress. +- **Logs**: structured log correlation with trace IDs. Exporters: - OTLP gRPC (default, port `4317`). -- OTLP HTTP (port `4318`) — set `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`. +- OTLP HTTP (port `4318`): set `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`. Send to an OpenTelemetry Collector, then fan out to Tempo / Jaeger / Honeycomb / Datadog / Grafana Cloud / whatever you run. @@ -136,13 +136,13 @@ GET /api/tasks/status Authorization: Bearer ``` -Returns an array of every scheduled / manual / watcher task with current status (`idle`, `queued`, `running`, `failed`) and last run time. Scrape this into your monitoring to alert on "Folder Scan hasn't run in 48 hours" — which usually means RQ workers are dead. +Returns an array of every scheduled / manual / watcher task with current status (`idle`, `queued`, `running`, `failed`) and last run time. Scrape this into your monitoring to alert on "Folder Scan hasn't run in 48 hours", which usually means RQ workers are dead. ## Anti-patterns - **Don't parse unstructured log lines** for metrics. Use OTEL or `/api/tasks/status`. - **Don't log at DEBUG in production.** The volume is real and scans will drown in it. -- **Don't scrape HTML pages for health checks.** `/api/heartbeat` is the contract — HTML changes between versions, the API endpoint is stable. +- **Don't scrape HTML pages for health checks.** `/api/heartbeat` is the contract; HTML changes between versions, the API endpoint is stable. ## Minimum recommended stack diff --git a/docs/administration/oidc/authelia.md b/docs/administration/oidc/authelia.md index e3c857de..c1f26069 100644 --- a/docs/administration/oidc/authelia.md +++ b/docs/administration/oidc/authelia.md @@ -1,13 +1,13 @@ --- title: OIDC with Authelia -description: Wire RomM's SSO to Authelia — claims policy, client registration, RomM env vars. +description: Wire RomM's SSO to Authelia: claims policy, client registration, RomM env vars. --- # OIDC with Authelia [Authelia](https://www.authelia.com/) is a lightweight open-source authentication and authorisation server with two-factor auth and SSO. Good fit for homelabs that already proxy through a reverse proxy. -Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -80,7 +80,7 @@ environment: - ROMM_BASE_URL=https://romm.example.com ``` -`OIDC_REDIRECT_URI` must match what you put in `redirect_uris` exactly — scheme, host, path, no trailing slash. +`OIDC_REDIRECT_URI` must match what you put in `redirect_uris` exactly: scheme, host, path, no trailing slash. For role mapping from Authelia groups, see [OIDC Setup → Role mapping](index.md#role-mapping-50). diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md index aadcd222..27752d4f 100644 --- a/docs/administration/oidc/authentik.md +++ b/docs/administration/oidc/authentik.md @@ -1,13 +1,13 @@ --- title: OIDC with Authentik -description: Wire RomM's SSO to Authentik — property mapping for email_verified, provider, application, RomM env vars. +description: Wire RomM's SSO to Authentik: property mapping for email_verified, provider, application, RomM env vars. --- # OIDC with Authentik [Authentik](https://goauthentik.io/) is a full-featured open-source IdP with MFA, flows, and a sizeable audit/admin surface. Good fit for users who want more than Authelia offers. -Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -55,7 +55,7 @@ Configure: - **Authorization flow**: implicit consent - **Redirect URIs**: `https://romm.example.com/api/oauth/openid` -Copy the generated **Client ID** and **Client Secret** — you'll use them as `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` on the RomM side. +Copy the generated **Client ID** and **Client Secret**; you'll use them as `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` on the RomM side. ![Provider settings](../../resources/authentik/4-provider-secrets.png) @@ -88,7 +88,7 @@ environment: - ROMM_BASE_URL=https://romm.example.com ``` -Note `OIDC_SERVER_APPLICATION_URL` points at the per-application URL (`/application/o/`) — not the Authentik root. +Note `OIDC_SERVER_APPLICATION_URL` points at the per-application URL (`/application/o/`), not the Authentik root. For role mapping from Authentik groups, see [OIDC Setup → Role mapping](index.md#role-mapping-50). @@ -100,7 +100,7 @@ In RomM → **Profile** → set your email to exactly the same address Authentik ## 7. Test -Restart RomM and open `/login`. Click the **Login with OIDC** button — you're redirected to Authentik, authenticate, and come back signed into RomM. +Restart RomM and open `/login`. Click the **Login with OIDC** button; you're redirected to Authentik, authenticate, and come back signed into RomM. ![Login with OIDC](../../resources/authentik/8-romm-login.png) diff --git a/docs/administration/oidc/index.md b/docs/administration/oidc/index.md index 8768723d..63b90418 100644 --- a/docs/administration/oidc/index.md +++ b/docs/administration/oidc/index.md @@ -5,7 +5,7 @@ description: Wire RomM up to an OpenID Connect provider for SSO and centralised # OIDC Setup -OpenID Connect (OIDC) lets users sign in to RomM through an external identity provider — Authelia, Authentik, Keycloak, PocketID, Zitadel, Okta, Auth0, or anything standards-compliant. Benefits: single sign-on across your homelab, no RomM-specific password to manage, centralised MFA, and on 5.0 you can map OIDC groups/claims to RomM roles. +OpenID Connect (OIDC) lets users sign in to RomM through an external identity provider: Authelia, Authentik, Keycloak, PocketID, Zitadel, Okta, Auth0, or anything standards-compliant. Benefits: single sign-on across your homelab, no RomM-specific password to manage, centralised MFA, and on 5.0 you can map OIDC groups/claims to RomM roles. !!! note "OIDC is optional" RomM has its own user system and works fine without OIDC. Enable OIDC when you already run an IdP and want RomM to follow suit. @@ -14,22 +14,22 @@ OpenID Connect (OIDC) lets users sign in to RomM through an external identity pr 1. User clicks the OIDC login button on `/login`. 2. RomM redirects them to your provider. -3. They authenticate (password, passkey, MFA — whatever your provider enforces). +3. They authenticate (password, passkey, MFA, whatever your provider enforces). 4. Provider redirects back to `{ROMM_BASE_URL}/api/oauth/openid` with an authorisation code. 5. RomM exchanges the code for an ID token, reads the user's email and role claims, and either creates a matching RomM user on the fly or logs an existing one in. -6. From there it's a normal RomM session — same cookies, same scope model. +6. From there it's a normal RomM session: same cookies, same scope model. ## Provider guides -Pick your provider and follow the step-by-step. They all end with the same set of env vars on the RomM side — the guides just differ on how to register RomM as an application and where to find the client ID/secret. +Pick your provider and follow the step-by-step. They all end with the same set of env vars on the RomM side; the guides just differ on how to register RomM as an application and where to find the client ID/secret. -- [Authelia](authelia.md) — lightweight self-hosted IdP, great for homelabs. -- [Authentik](authentik.md) — full-featured open-source IdP with MFA and fancy flows. -- [Keycloak](keycloak.md) — the heavyweight standard; feature-complete. -- [PocketID](pocketid.md) — passkey-only, minimalist. -- [Zitadel](zitadel.md) — enterprise-grade open source with SAML + OIDC. +- [Authelia](authelia.md): lightweight self-hosted IdP, great for homelabs. +- [Authentik](authentik.md): full-featured open-source IdP with MFA and fancy flows. +- [Keycloak](keycloak.md): the heavyweight standard; feature-complete. +- [PocketID](pocketid.md): passkey-only, minimalist. +- [Zitadel](zitadel.md): enterprise-grade open source with SAML + OIDC. -Not listed? Any standards-compliant OIDC provider works — Okta, Auth0, Google Workspace, Microsoft Entra, etc. Use one of the above as a template and consult your provider's docs for the registration side. +Not listed? Any standards-compliant OIDC provider works: Okta, Auth0, Google Workspace, Microsoft Entra, etc. Use one of the above as a template and consult your provider's docs for the registration side. ## Minimum RomM config @@ -46,7 +46,7 @@ environment: - ROMM_BASE_URL=https://romm.example.com # must match your reverse-proxy URL ``` -`OIDC_REDIRECT_URI` must exactly match what you register at the provider — same scheme, host, path, no trailing slash. +`OIDC_REDIRECT_URI` must exactly match what you register at the provider: same scheme, host, path, no trailing slash. ## Role mapping (5.0) @@ -60,13 +60,13 @@ environment: - OIDC_ROLE_ADMIN=romm-admin,platform-admins ``` -On every login, RomM reads the claim named by `OIDC_CLAIM_ROLES` (often `groups`, sometimes `realm_access.roles` on Keycloak — check your provider's token output). Whichever role has a matching value wins; if nothing matches, the user stays/becomes a Viewer. +On every login, RomM reads the claim named by `OIDC_CLAIM_ROLES` (often `groups`, sometimes `realm_access.roles` on Keycloak; check your provider's token output). Whichever role has a matching value wins; if nothing matches, the user stays/becomes a Viewer. Roles are re-evaluated on **every login**, so demoting someone on the IdP side takes effect the next time they sign in. ## Autologin -Bypass the RomM login page entirely — redirects straight to the IdP: +Bypass the RomM login page entirely; redirects straight to the IdP: ```yaml environment: @@ -102,12 +102,12 @@ environment: ## Important notes - **Email must match** between OIDC and any existing RomM account, otherwise OIDC creates a new account alongside the old one. -- **HTTPS is required** in production — OIDC will refuse to redirect to a plain-HTTP `ROMM_BASE_URL`. +- **HTTPS is required** in production: OIDC will refuse to redirect to a plain-HTTP `ROMM_BASE_URL`. - **Clock skew**: large drift between the RomM host and IdP will cause ID-token validation to fail. Run NTP. ## Troubleshooting Common failures and fixes live in [Authentication Troubleshooting](../../troubleshooting/authentication.md). Two of the usual suspects: -- `redirect_uri_mismatch` — `OIDC_REDIRECT_URI` differs from what's registered at the provider. Even a trailing slash matters. -- User created but stuck at Viewer — check `OIDC_CLAIM_ROLES` points at a claim that actually exists in the token, and the group names match exactly (case-sensitive). +- `redirect_uri_mismatch`: `OIDC_REDIRECT_URI` differs from what's registered at the provider. Even a trailing slash matters. +- User created but stuck at Viewer: check `OIDC_CLAIM_ROLES` points at a claim that actually exists in the token, and the group names match exactly (case-sensitive). diff --git a/docs/administration/oidc/keycloak.md b/docs/administration/oidc/keycloak.md index 1f0f1cc0..cd1fa8aa 100644 --- a/docs/administration/oidc/keycloak.md +++ b/docs/administration/oidc/keycloak.md @@ -1,13 +1,13 @@ --- title: OIDC with Keycloak -description: Wire RomM's SSO to Keycloak — realm, client, RomM env vars, optional role mapping. +description: Wire RomM's SSO to Keycloak: realm, client, RomM env vars, optional role mapping. --- # OIDC with Keycloak -[Keycloak](https://www.keycloak.org/) is the heavyweight open-source IAM standard. Feature-complete, well-documented, widely deployed — the default choice when your SSO needs are serious. +[Keycloak](https://www.keycloak.org/) is the heavyweight open-source IAM standard. Feature-complete, well-documented, widely deployed; the default choice when your SSO needs are serious. -Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -44,7 +44,7 @@ environment: - ROMM_BASE_URL=https://romm.example.com ``` -`OIDC_SERVER_APPLICATION_URL` must include the realm — `.../realms/` — not just the Keycloak root. +`OIDC_SERVER_APPLICATION_URL` must include the realm (`.../realms/`), not just the Keycloak root. ## 4. Set email on RomM + verify in Keycloak @@ -54,7 +54,7 @@ On the Keycloak side, **Admin Console → Users**: mark each RomM user's email a ## 5. Test -Restart RomM and open `/login`. Click **Login with Keycloak** — you're redirected to Keycloak, authenticate, and bounce back signed into RomM. +Restart RomM and open `/login`. Click **Login with Keycloak**; you're redirected to Keycloak, authenticate, and bounce back signed into RomM. If a user already exists in RomM with a matching email, they're signed into that account. Otherwise RomM creates a new account with Viewer permissions. diff --git a/docs/administration/oidc/pocketid.md b/docs/administration/oidc/pocketid.md index d0d7e426..cde555c6 100644 --- a/docs/administration/oidc/pocketid.md +++ b/docs/administration/oidc/pocketid.md @@ -5,9 +5,9 @@ description: Wire RomM's SSO to PocketID for passkey-only login. # OIDC with PocketID -[PocketID](https://github.com/stonith404/pocket-id) is a minimalist OIDC provider that **only** supports passkey authentication — no passwords. Good fit when you want passwordless login end-to-end without the complexity of Keycloak or Authentik. +[PocketID](https://github.com/stonith404/pocket-id) is a minimalist OIDC provider that **only** supports passkey authentication, with no passwords. Good fit when you want passwordless login end-to-end without the complexity of Keycloak or Authentik. -Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -17,12 +17,12 @@ PocketID installed, running, and your admin passkey already registered. Upstream In PocketID admin: -1. **Application Configuration** — make sure **Emails Verified** is ticked. RomM requires verified emails. +1. **Application Configuration**: make sure **Emails Verified** is ticked. RomM requires verified emails. 2. Go to **OIDC Client** → **Add OIDC Client**. 3. Fill in: - **Name**: `RomM` - **Callback URLs**: `https://romm.example.com/api/oauth/openid` -4. **Save**. Stay on this page — the client secret only displays **once**. Copy both the Client ID and Client Secret now. +4. **Save**. Stay on this page; the client secret only displays **once**. Copy both the Client ID and Client Secret now. ## 3. Configure RomM @@ -47,7 +47,7 @@ RomM → **Profile** → set your email to exactly the same address PocketID has ## 5. Test -Restart RomM and open `/login`. Click **Login with OIDC** — you're redirected to PocketID, tap your passkey, and bounce back signed into RomM. +Restart RomM and open `/login`. Click **Login with OIDC**; you're redirected to PocketID, tap your passkey, and bounce back signed into RomM. ![Login with OIDC](../../resources/pocketid/2-romm-login.png) diff --git a/docs/administration/oidc/zitadel.md b/docs/administration/oidc/zitadel.md index 6b124ad7..a39baabb 100644 --- a/docs/administration/oidc/zitadel.md +++ b/docs/administration/oidc/zitadel.md @@ -1,13 +1,13 @@ --- title: OIDC with Zitadel -description: Wire RomM's SSO to Zitadel — project, application, user info inside ID token, RomM env vars. +description: Wire RomM's SSO to Zitadel: project, application, user info inside ID token, RomM env vars. --- # OIDC with Zitadel [Zitadel](https://zitadel.com/) is an enterprise-grade open-source IAM platform supporting OAuth2, OIDC, SAML, and passwordless. Good fit when you want an enterprise-ish IdP without running Keycloak. -Before starting, read the [OIDC Setup overview](index.md) — it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -19,9 +19,9 @@ Create a new project (e.g. `RomM`). This holds the client and its auth settings. On the project's **General** tab, the toggles mean: -- **Assert Roles on Authentication** — not useful today; RomM 5.0 reads roles via `OIDC_CLAIM_ROLES` regardless. Leave off unless you're setting up role mapping. -- **Check authorization on Authentication** — recommended. If off, anyone who can register in Zitadel can sign into RomM (as Viewer). Turn this on if Zitadel registration is open. -- **Check for Project on Authentication** — only matters if you're separating users by Zitadel organization. Skip for a single RomM instance. +- **Assert Roles on Authentication**: not useful today; RomM 5.0 reads roles via `OIDC_CLAIM_ROLES` regardless. Leave off unless you're setting up role mapping. +- **Check authorization on Authentication**: recommended. If off, anyone who can register in Zitadel can sign into RomM (as Viewer). Turn this on if Zitadel registration is open. +- **Check for Project on Authentication**: only matters if you're separating users by Zitadel organization. Skip for a single RomM instance. ### 2.5 (Optional) Grant users to the project @@ -29,7 +29,7 @@ If you enabled **Check authorization on Authentication**: 1. **Authorization** tab → **New**. 2. Select user(s) → **Continue**. -3. "No role has been created yet" is fine — just **Save**. The user appears in the authorization list with no roles. +3. "No role has been created yet" is fine: just **Save**. The user appears in the authorization list with no roles. ## 3. Create the application @@ -43,7 +43,7 @@ On the project's **General** tab, under **Applications**, click **New**. Tick ** - **Redirect URIs**: `https://romm.example.com/api/oauth/openid` - **Post Logout URIs**: `https://romm.example.com/` -Click **Create**. The **client secret is shown once** — copy it now. +Click **Create**. The **client secret is shown once**; copy it now. ## 4. Enable claims in the ID Token @@ -64,7 +64,7 @@ environment: - ROMM_BASE_URL=https://romm.example.com ``` -Zitadel's OIDC discovery URL is at `/.well-known/openid-configuration` — handy for debugging. +Zitadel's OIDC discovery URL is at `/.well-known/openid-configuration`, handy for debugging. For role mapping from Zitadel, see [OIDC Setup → Role mapping](index.md#role-mapping-50). @@ -74,6 +74,6 @@ In RomM → **Profile** → set your email to exactly the same address your Zita ## 7. Test -Restart RomM and open `/login`. Click **Login with Zitadel** — you're redirected, authenticate, and bounce back signed into RomM. +Restart RomM and open `/login`. Click **Login with Zitadel**; you're redirected, authenticate, and bounce back signed into RomM. If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 1cd46bd6..d1e0490a 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -9,13 +9,13 @@ RomM keeps its catalogue in sync with your filesystem through three mechanisms: 1. **Manual scans** you trigger from the Scan page. 2. **Scheduled scans** (default: nightly) run by the task runner. -3. **The filesystem watcher** — live reaction to files landing in or leaving your library. +3. **The filesystem watcher**: live reaction to files landing in or leaving your library. All three share the same scan engine and the same set of **scan modes**. ## Scan modes -Every scan picks one mode. Modes differ in what they touch — use the most-targeted mode that accomplishes what you want. +Every scan picks one mode. Modes differ in what they touch; use the most-targeted mode that accomplishes what you want. | Mode | What it does | When to use | | --- | --- | --- | @@ -26,7 +26,7 @@ Every scan picks one mode. Modes differ in what they touch — use the most-targ | **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash (pre-4.4) or when you suspect file corruption. | | **Complete** | Full rescan. Recalculates hashes, re-fetches metadata for everything. | Rarely. Takes a long time. | -You can further scope a scan to specific **platforms** and specific **metadata providers** — useful when only one provider has changed (e.g. just enabled Hasheous → Unmatched scan, Hasheous selected, on all platforms). +You can further scope a scan to specific **platforms** and specific **metadata providers**, useful when only one provider has changed (e.g. just enabled Hasheous → Unmatched scan, Hasheous selected, on all platforms). ## Manual scans @@ -38,7 +38,7 @@ You can further scope a scan to specific **platforms** and specific **metadata p - A live log of everything the scanner is doing. - Per-platform progress panels with matched / unmatched / missing counts. -A running scan survives browser refreshes — the log streams over Socket.IO. Multiple admins opening the page see the same scan state. +A running scan survives browser refreshes; the log streams over Socket.IO. Multiple admins opening the page see the same scan state. ## Scheduled scans @@ -49,13 +49,13 @@ Configured via env vars (full table in the [Scheduled Tasks reference](../refere | `SCAN_INTERVAL_CRON` | `0 0 * * *` | Cron expression for the scheduled library scan. Runs a **Quick** scan by default. | | `SCAN_TIMEOUT_HOURS` | `1` | Hard cap. Scans that exceed this are killed and logged. | | `SCAN_WORKERS` | _auto_ | Concurrent worker processes for scanning. Leave as auto unless you're tuning. | -| `SEVEN_ZIP_TIMEOUT` | — | Per-archive timeout for `.7z` extraction during scan. Raise if scanning huge compressed ROM sets. | +| `SEVEN_ZIP_TIMEOUT` | _unset_ | Per-archive timeout for `.7z` extraction during scan. Raise if scanning huge compressed ROM sets. | To disable scheduled scans entirely, either unset the cron or set it to something unreachable (`SCAN_INTERVAL_CRON=0 0 31 2 *`). ## Filesystem watcher -The watcher tails your library folder and schedules scans in response to file events — files added, moved, deleted. It's off by default on some deployments; enable with: +The watcher tails your library folder and schedules scans in response to file events: files added, moved, deleted. It's off by default on some deployments; enable with: ```yaml environment: @@ -68,16 +68,16 @@ environment: Behaviour: - Watches `/romm/library` (and everything under it) recursively. -- Debounces bursts of events — the delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. +- Debounces bursts of events; the delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. - Batches scans intelligently: many events → a single consolidated scan, not one scan per file. -- Ignores content modifications and metadata-only changes — it cares about files appearing or disappearing, not about `chmod`. +- Ignores content modifications and metadata-only changes; it cares about files appearing or disappearing, not about `chmod`. - Skips OS noise (`.DS_Store`, `Thumbs.db`, `.tmp`, etc.). - If a whole new platform folder appears, switches to a **New Platforms** scan to pick it up cleanly. ### When **not** to enable the watcher - **Slow / high-latency filesystems** (SMB mounts, rclone mounts, anything not local disk). The watcher reacts to every event, and flaky mounts generate a lot. Use scheduled scans instead. -- **Libraries under active write load from other tools** (e.g. a ROM manager constantly tagging files). The watcher will re-scan on every change — at best noisy, at worst a scan loop. +- **Libraries under active write load from other tools** (e.g. a ROM manager constantly tagging files). The watcher will re-scan on every change: at best noisy, at worst a scan loop. ### Watcher vs scheduled scan @@ -87,7 +87,7 @@ Behaviour: | CPU cost | Only when files change | Constant cadence | | Works over SMB/NFS | Flakily | Reliably | | Catches renames | Yes | Yes | -| Survives a container restart | Yes — re-arms on startup | Yes | +| Survives a container restart | Yes, re-arms on startup | Yes | Run both. The watcher handles day-to-day additions; the scheduled scan is a safety net. @@ -127,7 +127,7 @@ When a metadata provider returns multiple regional variants (Japanese cover, US ## Metadata source priority -Who wins when two providers disagree — covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution). Short version: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. +Who wins when two providers disagree: covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution). Short version: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. ## Troubleshooting diff --git a/docs/administration/scheduled-tasks.md b/docs/administration/scheduled-tasks.md index af7be510..91d95678 100644 --- a/docs/administration/scheduled-tasks.md +++ b/docs/administration/scheduled-tasks.md @@ -7,12 +7,12 @@ description: What RomM runs in the background, how to reschedule it, and how to RomM runs background work through **RQ** (Redis Queue). Tasks fall into four categories: -- **Scheduled** — cron-driven, run on their own. -- **Watcher** — triggered by filesystem events. -- **Manual** — admin-triggered from the UI or API. -- **Enqueued** — side effects of user actions (a scan started from the Scan page, a metadata refresh on a ROM edit). Not listed here. +- **Scheduled**: cron-driven, run on their own. +- **Watcher**: triggered by filesystem events. +- **Manual**: admin-triggered from the UI or API. +- **Enqueued**: side effects of user actions (a scan started from the Scan page, a metadata refresh on a ROM edit). Not listed here. -For the lookup-only reference (every task, its cron default, its env var), see [Scheduled Tasks Reference](../reference/scheduled-tasks.md). This page is the narrative — what each one is for, when to worry, and how to tune. +For the lookup-only reference (every task, its cron default, its env var), see [Scheduled Tasks Reference](../reference/scheduled-tasks.md). This page is the narrative: what each one is for, when to worry, and how to tune. ## The full table @@ -28,10 +28,10 @@ minute hour day-of-month month day-of-week Examples: -- `0 3 * * *` — 3 AM daily. -- `0 */6 * * *` — every 6 hours, on the hour. -- `*/30 * * * *` — every 30 minutes. -- `0 2 * * 0` — 2 AM every Sunday. +- `0 3 * * *`: 3 AM daily. +- `0 */6 * * *`: every 6 hours, on the hour. +- `*/30 * * * *`: every 30 minutes. +- `0 2 * * 0`: 2 AM every Sunday. Set the env var, restart the container. Alembic runs on every start; the scheduler picks up the new schedule the moment RomM comes back up. @@ -40,7 +40,7 @@ Set the env var, restart the container. Alembic runs on every start; the schedul Two ways: - **The clean way**: set the cron to a time that essentially never fires. `0 0 31 2 *` (Feb 31st) works. -- **The feature-flag way**: some tasks have a dedicated enable toggle — e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false` disables the LaunchBox sync. +- **The feature-flag way**: some tasks have a dedicated enable toggle, e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false` disables the LaunchBox sync. Check the [env var reference](../reference/environment-variables.md) for which toggles exist. @@ -79,7 +79,7 @@ Advanced. Connect to Redis, inspect the `rq` queues, enqueue a job manually. Onl ### Folder Scan -The nightly library scan. Defaults to **Quick** mode — skips files already in the DB — so it's cheap even on big libraries. See [Scanning & Watcher](scanning-and-watcher.md) for the full scan mechanic. +The nightly library scan. Defaults to **Quick** mode (skips files already in the DB) so it's cheap even on big libraries. See [Scanning & Watcher](scanning-and-watcher.md) for the full scan mechanic. ### Switch titleDB Fetch @@ -87,7 +87,7 @@ Downloads an updated copy of the Nintendo Switch title ID database used for matc ### LaunchBox Metadata Sync -Refreshes the local LaunchBox metadata cache used when LaunchBox is enabled as a metadata provider. Nightly at 2 AM by default. Only useful if `LAUNCHBOX_API_ENABLED=true` — disable it otherwise to save a handful of CPU seconds. +Refreshes the local LaunchBox metadata cache used when LaunchBox is enabled as a metadata provider. Nightly at 2 AM by default. Only useful if `LAUNCHBOX_API_ENABLED=true`; disable it otherwise to save a handful of CPU seconds. ### Image Conversion @@ -99,7 +99,7 @@ Refreshes per-user RA progression. Nightly at 4 AM. Users see updated achievemen ### Netplay Cleanup -Sweeps orphaned netplay sessions — rooms where every participant has disconnected but the metadata lingers. Every 30 minutes by default. +Sweeps orphaned netplay sessions: rooms where every participant has disconnected but the metadata lingers. Every 30 minutes by default. ### Push-Pull Device Sync @@ -116,11 +116,11 @@ Deletes cover images, screenshots, and manuals no longer referenced by any ROM. ## Monitoring tasks - **Live**: Administration → Tasks page shows every task's current status (queued, running, idle, failed). -- **API**: `GET /api/tasks/status` for a JSON summary — wire this to an uptime monitor if you want alerts. +- **API**: `GET /api/tasks/status` for a JSON summary; wire this to an uptime monitor if you want alerts. - **Logs**: `docker logs romm` → look for `rq.worker` lines. - **Heartbeat**: `GET /api/heartbeat` returns overall health plus per-task summary; handy for monitoring dashboards. -A task that's been "running" for hours is usually a scan that hit `SCAN_TIMEOUT_HOURS` — the log will say so. Tasks that fail leave a stack trace in the container logs; the RQ `failed` queue retains the last few for inspection. +A task that's been "running" for hours is usually a scan that hit `SCAN_TIMEOUT_HOURS`; the log will say so. Tasks that fail leave a stack trace in the container logs; the RQ `failed` queue retains the last few for inspection. ## Tuning for small hosts @@ -128,5 +128,5 @@ Defaults assume you've got a reasonable box. On a Pi or NAS with 2 GB of RAM and - Raise the cron intervals (daily → weekly) for the nightlies. - Set `SCAN_WORKERS=1` to avoid concurrent scan processes. -- Enable the watcher but raise `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` to 60+ seconds — slower reaction, far less churn. +- Enable the watcher but raise `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` to 60+ seconds: slower reaction, far less churn. - Disable image conversion if you don't care about WebP (`IMAGE_CONVERSION_INTERVAL_CRON=0 0 31 2 *`). diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 6543ac3d..58b561d4 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -27,9 +27,9 @@ A breakdown of disk usage by directory: | Bucket | Maps to | Grows when | | --- | --- | --- | | **Library** | `/romm/library` | You add ROMs. Controlled by you, not RomM. | -| **Resources** | `/romm/resources` | Scans fetch cover art, screenshots, manuals. Safe to purge — can be rebuilt from a rescan. | +| **Resources** | `/romm/resources` | Scans fetch cover art, screenshots, manuals. Safe to purge; can be rebuilt from a rescan. | | **Assets** | `/romm/assets` | Users upload saves, states, screenshots. **Back this up.** | -| **Config** | `/romm/config` | Rarely — you or admins edit `config.yml`. | +| **Config** | `/romm/config` | Rarely: you or admins edit `config.yml`. | ### Per-platform breakdown @@ -38,7 +38,7 @@ Under the summary, an expandable table sorted by either ROM count or disk usage. - Matched / unmatched count. - Region distribution (how many games tagged USA, Japan, Europe, World, etc.). - Language distribution. -- Metadata coverage — how many games have each field populated (cover, description, release date, rating). +- Metadata coverage: how many games have each field populated (cover, description, release date, rating). Useful for: "which platform is eating my disk?" and "which platform has the worst match rate?" @@ -47,9 +47,9 @@ Useful for: "which platform is eating my disk?" and "which platform has the wors ## What the numbers don't include -- **Disk usage for the database itself** — MariaDB / Postgres data volume isn't reported here. Use `docker system df -v` or your volume backend's native tooling. -- **Redis memory** — same; monitor Redis separately if you're tight on RAM. -- **Per-user storage breakdown** — not exposed in 5.0. +- **Disk usage for the database itself**: MariaDB / Postgres data volume isn't reported here. Use `docker system df -v` or your volume backend's native tooling. +- **Redis memory**: same; monitor Redis separately if you're tight on RAM. +- **Per-user storage breakdown**: not exposed in 5.0. ## Using stats for capacity planning @@ -61,7 +61,7 @@ Rule-of-thumb sizes: | Assets / Library | <1% unless you have lots of heavy PSP/PS3 saves. | | DB size / Games | ~10-50 KB per ROM row, depending on metadata richness. | -If **Resources** is ballooning, run the [Cleanup Orphaned Resources](scheduled-tasks.md#cleanup-orphaned-resources-manual) task. If **Assets** is growing unexpectedly, a user is probably uploading save states for long-form JRPGs — that's fine, just plan for it. +If **Resources** is ballooning, run the [Cleanup Orphaned Resources](scheduled-tasks.md#cleanup-orphaned-resources-manual) task. If **Assets** is growing unexpectedly, a user is probably uploading save states for long-form JRPGs; that's fine, just plan for it. ## API @@ -77,8 +77,8 @@ Wire to your monitoring stack via the API rather than scraping the HTML page. Se ## Troubleshooting -- **Numbers look stale** — stats are computed on page load, not cached. Reload. If still stale, the DB connection is degraded; check `docker logs romm 2>&1 | grep -i database`. -- **Disk sizes look wrong** — RomM reports what it sees in `/romm/*`. If your compose mounts a path that's smaller than the host dataset (e.g. you mounted a sub-directory), RomM only sees that subset. -- **"Platform stats couldn't load"** — the DB query timed out. On very large libraries this happens; retry, or raise `SCAN_TIMEOUT_HOURS` (yes, it also gates the stats query). +- **Numbers look stale**: stats are computed on page load, not cached. Reload. If still stale, the DB connection is degraded; check `docker logs romm 2>&1 | grep -i database`. +- **Disk sizes look wrong**: RomM reports what it sees in `/romm/*`. If your compose mounts a path that's smaller than the host dataset (e.g. you mounted a sub-directory), RomM only sees that subset. +- **"Platform stats couldn't load"**: the DB query timed out. On very large libraries this happens; retry, or raise `SCAN_TIMEOUT_HOURS` (yes, it also gates the stats query). For anything else: [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/administration/ssh-sync.md b/docs/administration/ssh-sync.md index b59d2161..95e60eb3 100644 --- a/docs/administration/ssh-sync.md +++ b/docs/administration/ssh-sync.md @@ -5,12 +5,12 @@ description: Configure SSH key-based push/pull sync to handhelds and other devic # SSH Sync -RomM's Push-Pull Device Sync task can push saves/states to registered devices and pull them back after a session — over SSH, using a key that RomM holds. This page covers the server-side setup. +RomM's Push-Pull Device Sync task can push saves/states to registered devices and pull them back after a session, over SSH, using a key that RomM holds. This page covers the server-side setup. -The client side — a handheld running Grout, a SteamDeck running DeckRommSync, etc. — lives in [Integrations & Ecosystem](../ecosystem/index.md). The wire protocol (API-level sync negotiation, play-session ingest) lives in [Device Sync Protocol](../ecosystem/device-sync-protocol.md). +The client side (a handheld running Grout, a SteamDeck running DeckRommSync, etc.) lives in [Integrations & Ecosystem](../ecosystem/index.md). The wire protocol (API-level sync negotiation, play-session ingest) lives in [Device Sync Protocol](../ecosystem/device-sync-protocol.md). !!! note "Most companion apps don't need this" - Argosy, Playnite, and most mobile/desktop clients sync via HTTPS + Client API Tokens. SSH sync is specifically for handhelds and similar devices where RomM pushes files to a filesystem the device exposes via SSH — Grout on muOS / NextUI being the canonical case. + Argosy, Playnite, and most mobile/desktop clients sync via HTTPS + Client API Tokens. SSH sync is specifically for handhelds and similar devices where RomM pushes files to a filesystem the device exposes via SSH, with Grout on muOS / NextUI being the canonical case. ## When you need SSH sync @@ -18,7 +18,7 @@ The client side — a handheld running Grout, a SteamDeck running DeckRommSync, - You want RomM to automatically copy saves/states to the device and pull them back when a session ends. - Your sync runs on a schedule, not on-demand. -If none of that applies, you don't need this page — HTTPS + Client API Tokens is simpler and more flexible. +If none of that applies, you don't need this page. HTTPS + Client API Tokens is simpler and more flexible. ## Configuring the server @@ -45,7 +45,7 @@ services: Keep it read-only. RomM doesn't need to modify the key. -`SSH_PRIVATE_KEY_PATH` can point anywhere inside the container — conventionally `/romm/.ssh/id_rsa`. +`SSH_PRIVATE_KEY_PATH` can point anywhere inside the container, conventionally `/romm/.ssh/id_rsa`. ### 3. Restart RomM @@ -61,7 +61,7 @@ For each handheld / device you want to sync: ### 1. Authorise the RomM key -Copy the **public** key (`~/romm-sync-key.pub` from step 1 above) to the device's `~/.ssh/authorized_keys`. Exactly how depends on the device — Grout on muOS has a helper, others expose a filesystem you can `ssh-copy-id` into. +Copy the **public** key (`~/romm-sync-key.pub` from step 1 above) to the device's `~/.ssh/authorized_keys`. Exactly how depends on the device: Grout on muOS has a helper, others expose a filesystem you can `ssh-copy-id` into. ### 2. Register the device in RomM @@ -80,7 +80,7 @@ From the RomM container, confirm SSH works: docker exec romm ssh -i "$SSH_PRIVATE_KEY_PATH" user@ echo ok ``` -You should see `ok`. If you see a host-key prompt, accept it — RomM will remember it in its `known_hosts`. If you see `permission denied`, the authorised key isn't installed correctly. +You should see `ok`. If you see a host-key prompt, accept it; RomM will remember it in its `known_hosts`. If you see `permission denied`, the authorised key isn't installed correctly. ## How sync runs @@ -98,10 +98,10 @@ Disable sync for a specific device by deregistering it from **Administration → ## Troubleshooting -- **`Permission denied (publickey)`** — authorised key isn't set up on the device, or the private key inside the container can't be read (check the file permissions and bind-mount flags). -- **`Host key verification failed`** — the device's host key changed (after a reinstall, typically). Shell into the container and remove the offending line from `~/.ssh/known_hosts`. -- **Sync silently doesn't run** — check `GET /api/tasks/status` for the Push-Pull task's state. "failed" points you at the error; "never ran" means the cron isn't firing (see [Scheduled Tasks](scheduled-tasks.md)). -- **Connection times out** — the device is offline or the network path is blocked. Confirm reachability from the RomM container: `docker exec romm ping `. +- **`Permission denied (publickey)`**: authorised key isn't set up on the device, or the private key inside the container can't be read (check the file permissions and bind-mount flags). +- **`Host key verification failed`**: the device's host key changed (after a reinstall, typically). Shell into the container and remove the offending line from `~/.ssh/known_hosts`. +- **Sync silently doesn't run**: check `GET /api/tasks/status` for the Push-Pull task's state. "failed" points you at the error; "never ran" means the cron isn't firing (see [Scheduled Tasks](scheduled-tasks.md)). +- **Connection times out**: the device is offline or the network path is blocked. Confirm reachability from the RomM container: `docker exec romm ping `. More at [Device Sync Troubleshooting](../troubleshooting/sync.md). diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index 7157dea2..62508b65 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -15,7 +15,7 @@ RomM is multi-user from the start. The first user created during Setup is always | **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | | **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | -Roles are a convenience layer on top of **scopes** — see the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0; if you need finer-grained access, use the role that's most restrictive and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. +Roles are a convenience layer on top of **scopes**; see the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0; if you need finer-grained access, use the role that's most restrictive and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. ## Scope matrix @@ -32,16 +32,16 @@ RomM authorisation is scope-based. Every API call and UI action maps to one or m | `assets.read` | View own saves/states/screenshots | ✓ | ✓ | ✓ | | `assets.write` | Upload saves/states/screenshots | ✓ | ✓ | ✓ | | `collections.read` | Browse collections | ✓ | ✓ | ✓ | -| `collections.write` | Create/edit collections | — | ✓ | ✓ | -| `roms.write` | Edit ROM metadata | — | ✓ | ✓ | -| `platforms.write` | Edit/create platforms | — | ✓ | ✓ | -| `firmware.read` | List firmware | — | ✓ | ✓ | -| `firmware.write` | Upload/delete firmware | — | ✓ | ✓ | +| `collections.write` | Create/edit collections | - | ✓ | ✓ | +| `roms.write` | Edit ROM metadata | - | ✓ | ✓ | +| `platforms.write` | Edit/create platforms | - | ✓ | ✓ | +| `firmware.read` | List firmware | - | ✓ | ✓ | +| `firmware.write` | Upload/delete firmware | - | ✓ | ✓ | | `devices.read` | View own paired devices | ✓ | ✓ | ✓ | | `devices.write` | Manage own paired devices | ✓ | ✓ | ✓ | -| `users.read` | List all users | — | — | ✓ | -| `users.write` | Create/edit/delete users | — | — | ✓ | -| `tasks.run` | Trigger background tasks (scan, cleanup, etc.) | — | — | ✓ | +| `users.read` | List all users | - | - | ✓ | +| `users.write` | Create/edit/delete users | - | - | ✓ | +| `tasks.run` | Trigger background tasks (scan, cleanup, etc.) | - | - | ✓ | ## Creating users @@ -57,15 +57,15 @@ Better when you don't want to handle someone else's password. 1. **Administration → Users → Invite.** Pick a role; RomM generates a single-use invite link. 2. Send the link. The recipient opens it, picks their own username and password, and is logged in. -3. Invite links expire — the default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). +3. Invite links expire; the default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). ### Public self-registration -Off by default. To let anyone with the URL register their own Viewer account, set `ALLOW_PUBLIC_REGISTRATION=true`. Only enable this if your instance is behind auth at the reverse-proxy layer (Authelia, etc.) or you genuinely want open registration — once on, anyone who reaches `/register` can create an account. +Off by default. To let anyone with the URL register their own Viewer account, set `ALLOW_PUBLIC_REGISTRATION=true`. Only enable this if your instance is behind auth at the reverse-proxy layer (Authelia, etc.) or you genuinely want open registration. Once on, anyone who reaches `/register` can create an account. ### OIDC -If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md) — look for `OIDC_CLAIM_ROLES` and the per-role env vars. +If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md); look for `OIDC_CLAIM_ROLES` and the per-role env vars. ## Editing and deleting users diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index 42702dc7..ff5401fb 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -1,6 +1,6 @@ --- title: API Authentication -description: How to authenticate to the RomM REST API — session cookies, Basic, OAuth2 tokens, client tokens, and OIDC. +description: How to authenticate to the RomM REST API: session cookies, Basic, OAuth2 tokens, client tokens, and OIDC. --- # API Authentication @@ -15,7 +15,7 @@ RomM's REST API accepts four authentication modes. Pick the one that matches you | **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | | **OIDC session** | Users who sign in via SSO | Same as session cookie, but issued after OIDC callback | -All of them resolve to the same scope model — see the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. +All of them resolve to the same scope model; see the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. ## Base URL @@ -46,7 +46,7 @@ For OIDC logins, hitting `/api/auth/logout` also triggers RP-Initiated Logout if ## HTTP Basic -Fine for quick scripts. Avoid in shared environments — the credentials are sent on every request. +Fine for quick scripts. Avoid in shared environments; the credentials are sent on every request. ```bash curl -u alice:s3cret https://romm.example.com/api/roms @@ -94,11 +94,11 @@ Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=eyJhbGciOi... ``` -Request only the scopes you need — RomM will issue a token with the intersection of what you asked for and what the user has. +Request only the scopes you need; RomM will issue a token with the intersection of what you asked for and what the user has. ## Client API tokens (for companion apps) -For anything long-lived — a running companion app, a cron job, a CI integration — use **Client API Tokens** instead of OAuth2. They're issued per-user from **Administration → Client API Tokens**, carry a subset of the user's scopes, and don't expire unless you set an expiry. +For anything long-lived (a running companion app, a cron job, a CI integration), use **Client API Tokens** instead of OAuth2. They're issued per-user from **Administration → Client API Tokens**, carry a subset of the user's scopes, and don't expire unless you set an expiry. Token format: `rmm_` + 40 hex chars. Use it as a bearer: @@ -107,11 +107,11 @@ curl -H "Authorization: Bearer rmm_abcdef0123456789..." \ https://romm.example.com/api/roms ``` -Each user gets up to 25 active tokens. Tokens can be paired with a device via the [pairing flow](../ecosystem/client-api-tokens.md) — useful when you don't want to type a long token on a handheld. +Each user gets up to 25 active tokens. Tokens can be paired with a device via the [pairing flow](../ecosystem/client-api-tokens.md), useful when you don't want to type a long token on a handheld. ## OIDC -Users signing in through an OIDC provider get a regular RomM session, same as username/password login. For the API side this means you can't use an OIDC access token directly — authenticate the user through the browser first (they'll be redirected to the OIDC provider, then back to RomM), then use the resulting session cookie, **or** mint a Client API Token for programmatic use. +Users signing in through an OIDC provider get a regular RomM session, same as username/password login. For the API side this means you can't use an OIDC access token directly: authenticate the user through the browser first (they'll be redirected to the OIDC provider, then back to RomM), then use the resulting session cookie, **or** mint a Client API Token for programmatic use. OIDC provider setup lives in [Administration → OIDC](../administration/oidc/index.md). @@ -123,7 +123,7 @@ Every endpoint in the [API Reference](api-reference.md) lists its required scope - **Write**-ish endpoints want `*.write`. - **Admin** endpoints (anything under `/api/users` beyond `me`, `/api/tasks/run`) want `users.read`, `users.write`, or `tasks.run`. -A token that holds `users.write` also implicitly grants lesser scopes like `users.read` or `me.read` — RomM doesn't require you to list them all. But a token that holds only `roms.read` can't write, even if the underlying user is an Admin. +A token that holds `users.write` also implicitly grants lesser scopes like `users.read` or `me.read`; RomM doesn't require you to list them all. But a token that holds only `roms.read` can't write, even if the underlying user is an Admin. ## Errors @@ -131,12 +131,12 @@ A token that holds `users.write` also implicitly grants lesser scopes like `user | --- | --- | | `401 Unauthorized` | No credential, expired credential, bad credential. | | `403 Forbidden` | Authenticated, but the identity lacks a required scope. | -| `404 Not Found` | The resource doesn't exist — or, for privacy, the identity can't see it. | +| `404 Not Found` | The resource doesn't exist, or, for privacy, the identity can't see it. | When debugging a 403, check: 1. The **user's role** in Administration → Users. -2. The **token's scopes** (for OAuth2/Client API Tokens) — scopes are narrower than the user's role by default. +2. The **token's scopes** (for OAuth2/Client API Tokens); scopes are narrower than the user's role by default. 3. The endpoint's scope requirements in the [API Reference](api-reference.md). ## OpenAPI diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md index b8f8ec3c..08154f99 100644 --- a/docs/developers/api-reference.md +++ b/docs/developers/api-reference.md @@ -1,18 +1,18 @@ --- title: API Reference -description: Catalogue of RomM's REST API — authoritative interactive docs live on each instance. +description: Catalogue of RomM's REST API; authoritative interactive docs live on each instance. --- # API Reference -RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth — this page catalogues what's there, but every running RomM instance serves its own interactive browser with live "try it out" functionality. +RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth; this page catalogues what's there, but every running RomM instance serves its own interactive browser with live "try it out" functionality. ## Interactive docs Every RomM instance hosts two renderings of its own spec: -- **Swagger UI** — `{romm_url}/api/docs` — explore + try endpoints inline. -- **ReDoc** — `{romm_url}/api/redoc` — cleaner reading layout. +- **Swagger UI** at `{romm_url}/api/docs`: explore + try endpoints inline. +- **ReDoc** at `{romm_url}/api/redoc`: cleaner reading layout. The raw spec: @@ -32,11 +32,11 @@ For code generation, Postman imports, and schema-validation libraries, see [Cons Every write endpoint (and most read endpoints) requires a credential. Five modes supported: -- **Session cookie** — from the web UI. -- **HTTP Basic** — username + password header. -- **OAuth2 Bearer** — JWT access token. -- **Client API Token** — long-lived `rmm_...` bearer token. -- **OIDC session** — after IdP callback, same as a regular session. +- **Session cookie:** from the web UI. +- **HTTP Basic:** username + password header. +- **OAuth2 Bearer:** JWT access token. +- **Client API Token:** long-lived `rmm_...` bearer token. +- **OIDC session:** after IdP callback, same as a regular session. Full walkthrough in [API Authentication](api-authentication.md). @@ -46,138 +46,138 @@ Every endpoint belongs to a group. Summaries below; the interactive docs at `/ap ### Auth & users -- `POST /api/auth/login`, `/api/auth/logout` — session login/logout. -- `POST /api/token` — OAuth2 token endpoint (password + refresh grants). -- `GET /api/auth/openid`, `/api/oauth/openid` — OIDC flow. -- `POST /api/auth/forgot-password`, `/api/auth/reset-password` — password reset. -- `GET/POST/PUT/DELETE /api/users` — user management. See [Users & Roles](../administration/users-and-roles.md). -- `POST /api/users/register` — claim an invite link. -- `POST /api/users/invite-link` — generate one. +- `POST /api/auth/login`, `/api/auth/logout`: session login/logout. +- `POST /api/token`: OAuth2 token endpoint (password + refresh grants). +- `GET /api/auth/openid`, `/api/oauth/openid`: OIDC flow. +- `POST /api/auth/forgot-password`, `/api/auth/reset-password`: password reset. +- `GET/POST/PUT/DELETE /api/users`: user management. See [Users & Roles](../administration/users-and-roles.md). +- `POST /api/users/register`: claim an invite link. +- `POST /api/users/invite-link`: generate one. ### ROMs -- `GET /api/roms` — list with filters, pagination, search. -- `GET /api/roms/{id}` — single ROM details. -- `POST /api/roms/upload/{start,chunk,complete,cancel}` — chunked upload. -- `PUT /api/roms/{id}` — update metadata. -- `PUT /api/roms/{id}/props` — update per-user data (rating, status). -- `POST /api/roms/delete` — bulk delete. -- `GET /api/roms/{id}/content/{filename}` — download. -- `GET /api/roms/download?ids=...` — bulk zip download. -- `GET /api/roms/by-hash`, `/api/roms/by-metadata-provider` — lookup helpers. +- `GET /api/roms`: list with filters, pagination, search. +- `GET /api/roms/{id}`: single ROM details. +- `POST /api/roms/upload/{start,chunk,complete,cancel}`: chunked upload. +- `PUT /api/roms/{id}`: update metadata. +- `PUT /api/roms/{id}/props`: update per-user data (rating, status). +- `POST /api/roms/delete`: bulk delete. +- `GET /api/roms/{id}/content/{filename}`: download. +- `GET /api/roms/download?ids=...`: bulk zip download. +- `GET /api/roms/by-hash`, `/api/roms/by-metadata-provider`: lookup helpers. ### ROM notes, files, manuals -- `GET/POST/PUT/DELETE /api/roms/{id}/notes` — per-ROM notes (optional public). -- `GET /api/roms/{id}/files/content/{filename}` — individual file in a multi-file ROM. -- `POST/DELETE /api/roms/{id}/manuals` — PDF manual management. +- `GET/POST/PUT/DELETE /api/roms/{id}/notes`: per-ROM notes (optional public). +- `GET /api/roms/{id}/files/content/{filename}`: individual file in a multi-file ROM. +- `POST/DELETE /api/roms/{id}/manuals`: PDF manual management. ### Platforms -- `GET /api/platforms` — list. -- `GET /api/platforms/supported` — everything RomM recognises (same data as [Supported Platforms](../platforms/supported-platforms.md)). -- `PUT/DELETE /api/platforms/{id}` — edit or delete. +- `GET /api/platforms`: list. +- `GET /api/platforms/supported`: everything RomM recognises (same data as [Supported Platforms](../platforms/supported-platforms.md)). +- `PUT/DELETE /api/platforms/{id}`: edit or delete. ### Collections -- `GET/POST/PUT/DELETE /api/collections` — standard collections. -- `POST/DELETE /api/collections/{id}/roms` — add/remove ROMs. -- `GET/POST/PUT/DELETE /api/collections/smart` — [Smart Collections](../using/smart-collections.md). -- `GET /api/collections/virtual` — read-only [Virtual Collections](../using/virtual-collections.md). +- `GET/POST/PUT/DELETE /api/collections`: standard collections. +- `POST/DELETE /api/collections/{id}/roms`: add/remove ROMs. +- `GET/POST/PUT/DELETE /api/collections/smart`: [Smart Collections](../using/smart-collections.md). +- `GET /api/collections/virtual`: read-only [Virtual Collections](../using/virtual-collections.md). -### Assets — saves, states, screenshots +### Assets: saves, states, screenshots -- `GET/POST/PUT /api/saves` — save files. See [Saves & States](../using/saves-and-states.md). -- `POST /api/saves/delete` — bulk delete. -- `GET/POST/PUT /api/states` — emulator states. -- `POST /api/screenshots` — screenshot upload. +- `GET/POST/PUT /api/saves`: save files. See [Saves & States](../using/saves-and-states.md). +- `POST /api/saves/delete`: bulk delete. +- `GET/POST/PUT /api/states`: emulator states. +- `POST /api/screenshots`: screenshot upload. ### Firmware -- `GET/POST/DELETE /api/firmware` — firmware management. See [Firmware Management](../administration/firmware-management.md). -- `GET /api/firmware/{id}/content/{filename}` — download. +- `GET/POST/DELETE /api/firmware`: firmware management. See [Firmware Management](../administration/firmware-management.md). +- `GET /api/firmware/{id}/content/{filename}`: download. ### Devices & sync -- `GET/POST/PUT/DELETE /api/devices` — registered device management. -- `POST /api/sync/negotiate` — sync-session negotiation. -- `POST /api/sync/sessions/{id}/complete` — close a sync session with ingested play sessions. -- `POST /api/devices/{id}/push-pull` — trigger manual sync. +- `GET/POST/PUT/DELETE /api/devices`: registered device management. +- `POST /api/sync/negotiate`: sync-session negotiation. +- `POST /api/sync/sessions/{id}/complete`: close a sync session with ingested play sessions. +- `POST /api/devices/{id}/push-pull`: trigger manual sync. See [Device Sync Protocol](../ecosystem/device-sync-protocol.md) for the wire-level walkthrough. ### Play sessions -- `GET/POST/DELETE /api/play-sessions` — ingest and query play sessions (up to 100 per POST). +- `GET/POST/DELETE /api/play-sessions`: ingest and query play sessions (up to 100 per POST). ### Client API tokens -- `GET/POST/DELETE /api/client-tokens` — user's tokens. -- `PUT /api/client-tokens/{id}/regenerate` — regenerate the secret. -- `POST /api/client-tokens/{id}/pair` — generate a pairing code. -- `POST /api/client-tokens/exchange` — exchange a pairing code for a token. -- `GET /api/client-tokens/pair/{code}/status` — poll pairing status. -- `GET /api/client-tokens/all`, `DELETE /api/client-tokens/{id}/admin` — admin-only. +- `GET/POST/DELETE /api/client-tokens`: user's tokens. +- `PUT /api/client-tokens/{id}/regenerate`: regenerate the secret. +- `POST /api/client-tokens/{id}/pair`: generate a pairing code. +- `POST /api/client-tokens/exchange`: exchange a pairing code for a token. +- `GET /api/client-tokens/pair/{code}/status`: poll pairing status. +- `GET /api/client-tokens/all`, `DELETE /api/client-tokens/{id}/admin`: admin-only. See [Client API Tokens](../ecosystem/client-api-tokens.md) for the full flow. ### Search -- `GET /api/search/roms` — search across metadata providers (IGDB, Moby, SS, Flashpoint, LaunchBox). -- `GET /api/search/cover` — alternate cover search via SteamGridDB. +- `GET /api/search/roms`: search across metadata providers (IGDB, Moby, SS, Flashpoint, LaunchBox). +- `GET /api/search/cover`: alternate cover search via SteamGridDB. ### Tasks -- `GET /api/tasks`, `/api/tasks/status` — what's registered, what's running. -- `POST /api/tasks/run/{task_name}` — trigger on demand (requires `tasks.run`). +- `GET /api/tasks`, `/api/tasks/status`: what's registered, what's running. +- `POST /api/tasks/run/{task_name}`: trigger on demand (requires `tasks.run`). ### Configuration -- `GET /api/config` — current config (parts of it public, parts require auth). -- `POST/DELETE /api/config/system/platforms` — add/remove platform bindings. -- `POST/DELETE /api/config/system/versions` — add/remove version mappings. -- `POST/DELETE /api/config/exclude` — add/remove exclusion rules. +- `GET /api/config`: current config (parts of it public, parts require auth). +- `POST/DELETE /api/config/system/platforms`: add/remove platform bindings. +- `POST/DELETE /api/config/system/versions`: add/remove version mappings. +- `POST/DELETE /api/config/exclude`: add/remove exclusion rules. ### Stats -- `GET /api/stats` — aggregate counts + disk usage. -- `GET /api/stats?include_platform_stats=true` — per-platform breakdown. +- `GET /api/stats`: aggregate counts + disk usage. +- `GET /api/stats?include_platform_stats=true`: per-platform breakdown. ### Feeds -- `GET /api/feeds/webrcade` — WebRcade. -- `GET /api/feeds/tinfoil` — Nintendo Switch. -- `GET /api/feeds/pkgi/{psvita|psp}/{game|dlc}` — PS Vita / PSP. -- `GET /api/feeds/fpkgi/{ps4|ps5}` — PS4 / PS5. -- `GET /api/feeds/kekatsu/{nds}` — Nintendo DS. -- `GET /api/feeds/pkgj/{psx|psvita|psp}` — legacy pkgj. +- `GET /api/feeds/webrcade`: WebRcade. +- `GET /api/feeds/tinfoil`: Nintendo Switch. +- `GET /api/feeds/pkgi/{psvita|psp}/{game|dlc}`: PS Vita / PSP. +- `GET /api/feeds/fpkgi/{ps4|ps5}`: PS4 / PS5. +- `GET /api/feeds/kekatsu/{nds}`: Nintendo DS. +- `GET /api/feeds/pkgj/{psx|psvita|psp}`: legacy pkgj. See [Feeds](../reference/feeds.md) for format details per feed. ### Exports -- `POST /api/export/gamelist-xml` — ES-DE / Batocera format. -- `POST /api/export/pegasus` — Pegasus frontend format. +- `POST /api/export/gamelist-xml`: ES-DE / Batocera format. +- `POST /api/export/pegasus`: Pegasus frontend format. See [Exports](../reference/exports.md). ### Heartbeat -- `GET /api/heartbeat` — health + config snapshot. Safe to scrape from uptime monitors. -- `GET /api/heartbeat/metadata/{provider}` — per-provider health. +- `GET /api/heartbeat`: health + config snapshot. Safe to scrape from uptime monitors. +- `GET /api/heartbeat/metadata/{provider}`: per-provider health. ### Raw -- `GET /api/raw/assets/{path}` — direct asset passthrough for advanced integrations. +- `GET /api/raw/assets/{path}`: direct asset passthrough for advanced integrations. ### Netplay -- `GET /api/netplay/list?game_id=...` — active Netplay rooms for a game. -- WebSocket at `/netplay/socket.io` — room coordination. See [WebSockets](websockets.md). +- `GET /api/netplay/list?game_id=...`: active Netplay rooms for a game. +- WebSocket at `/netplay/socket.io`: room coordination. See [WebSockets](websockets.md). ## WebSockets -REST isn't the only surface. Two Socket.IO endpoints cover live-update and coordination use cases — [WebSockets](websockets.md). +REST isn't the only surface. Two Socket.IO endpoints cover live-update and coordination use cases: [WebSockets](websockets.md). ## Versioning @@ -191,8 +191,8 @@ For reproducible builds, pin the OpenAPI spec version at the same RomM version y ## See also -- [API Authentication](api-authentication.md) — auth modes in detail. -- [Consuming OpenAPI](openapi.md) — codegen + schema validation. -- [WebSockets](websockets.md) — Socket.IO endpoints. -- [Client API Tokens](../ecosystem/client-api-tokens.md) — recommended companion-app auth. -- [Device Sync Protocol](../ecosystem/device-sync-protocol.md) — sync endpoints in depth. +- [API Authentication](api-authentication.md): auth modes in detail. +- [Consuming OpenAPI](openapi.md): codegen + schema validation. +- [WebSockets](websockets.md): Socket.IO endpoints. +- [Client API Tokens](../ecosystem/client-api-tokens.md): recommended companion-app auth. +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): sync endpoints in depth. diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index 79e1a4df..eb1781e8 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -1,6 +1,6 @@ --- title: Architecture -description: High-level walkthrough of the RomM codebase — backend, frontend, nginx, workers. +description: High-level walkthrough of the RomM codebase: backend, frontend, nginx, workers. --- # Architecture @@ -21,15 +21,15 @@ rommapp/romm Key sub-directories: -- `backend/routers/` — FastAPI route definitions, one file per resource group. -- `backend/handlers/` — business logic (scan engine, metadata providers, auth). -- `backend/tasks/` — RQ job definitions (scheduled + manual + watcher tasks). -- `backend/config/` — Pydantic config schemas for `config.yml`. -- `backend/alembic/` — DB migrations. -- `backend/romm_test/` — test suite. -- `frontend/src/views/` — top-level Vue pages. -- `frontend/src/console/` — Console Mode SPA (separate entry). -- `frontend/src/stores/` — Pinia state stores. +- `backend/routers/`: FastAPI route definitions, one file per resource group. +- `backend/handlers/`: business logic (scan engine, metadata providers, auth). +- `backend/tasks/`: RQ job definitions (scheduled + manual + watcher tasks). +- `backend/config/`: Pydantic config schemas for `config.yml`. +- `backend/alembic/`: DB migrations. +- `backend/romm_test/`: test suite. +- `frontend/src/views/`: top-level Vue pages. +- `frontend/src/console/`: Console Mode SPA (separate entry). +- `frontend/src/stores/`: Pinia state stores. ## Runtime topology @@ -65,12 +65,12 @@ A running RomM container hosts several cooperating processes: ### The parts -- **nginx** — listens on `8080`. Serves static assets (the SPA bundle, EmulatorJS core files, Ruffle, cover images). Proxies API + WebSocket traffic to gunicorn. Streams large downloads via `mod_zip`. -- **gunicorn** — the Python WSGI server, running the FastAPI app. Multiple worker processes; `WEB_SERVER_CONCURRENCY` tunes count. -- **FastAPI backend** — routes, handlers, DB access via SQLAlchemy, Redis for sessions + cache + queue. -- **RQ workers** — separate process(es) that pop jobs off Redis queues and run them. Scans, metadata syncs, cleanup tasks. -- **Redis / Valkey** — in-container by default (full image), externalisable. -- **MariaDB / Postgres / MySQL / SQLite** — always external (or a separate container). +- **nginx:** listens on `8080`. Serves static assets (the SPA bundle, EmulatorJS core files, Ruffle, cover images). Proxies API + WebSocket traffic to gunicorn. Streams large downloads via `mod_zip`. +- **gunicorn:** the Python WSGI server, running the FastAPI app. Multiple worker processes; `WEB_SERVER_CONCURRENCY` tunes count. +- **FastAPI backend:** routes, handlers, DB access via SQLAlchemy, Redis for sessions + cache + queue. +- **RQ workers:** separate process(es) that pop jobs off Redis queues and run them. Scans, metadata syncs, cleanup tasks. +- **Redis / Valkey:** in-container by default (full image), externalisable. +- **MariaDB / Postgres / MySQL / SQLite:** always external (or a separate container). ## Request lifecycle @@ -113,7 +113,7 @@ Large uploads go through the chunked-upload flow for a reason: gunicorn's per-re - **Vue 3 + Composition API**. - **Vuetify 3** for UI components. - **Vite** for build + dev server with HMR. -- **Pinia** for state management — one store per resource family (auth, roms, platforms, collections, etc.). +- **Pinia** for state management: one store per resource family (auth, roms, platforms, collections, etc.). - **vue-i18n** for localisation. Translations in `src/locales//`. - **socket.io-client** for live updates. @@ -131,11 +131,11 @@ Separate SPA, compiled as a second bundle. Entry point at `/console`. Built with RQ has three priority queues: -- **high** — user-triggered scans, manual tasks. Get fast. -- **default** — scheduled nightlies, sync operations. -- **low** — cleanup, image conversion. Run when the system's idle. +- **high:** user-triggered scans, manual tasks. Get fast. +- **default:** scheduled nightlies, sync operations. +- **low:** cleanup, image conversion. Run when the system's idle. -All queues share the same worker pool. Jobs are Redis-backed, so restarting RomM doesn't lose in-flight work (appendonly needs to be on — see [Redis or Valkey](../install/redis-or-valkey.md)). +All queues share the same worker pool. Jobs are Redis-backed, so restarting RomM doesn't lose in-flight work (appendonly needs to be on; see [Redis or Valkey](../install/redis-or-valkey.md)). ## Auth @@ -147,9 +147,9 @@ Client API Tokens are stored as hash-only in the DB (we never store the plaintex ## Observability -- **Sentry** (opt-in) — unhandled exceptions. -- **OpenTelemetry** (opt-in) — traces, metrics, logs via OTLP. -- **`/api/heartbeat`** — aggregated health snapshot. +- **Sentry** (opt-in): unhandled exceptions. +- **OpenTelemetry** (opt-in): traces, metrics, logs via OTLP. +- **`/api/heartbeat`:** aggregated health snapshot. See [Observability](../administration/observability.md). @@ -165,11 +165,11 @@ See [Observability](../administration/observability.md). See [Contributing](contributing.md) for the process + style expectations. -For large changes, read the relevant handler in `backend/handlers/` first — the patterns there will guide what you write. +For large changes, read the relevant handler in `backend/handlers/` first; the patterns there will guide what you write. ## See also -- [Development Setup](development-setup.md) — get a local env running. -- [API Reference](api-reference.md) — what the backend exposes. -- [WebSockets](websockets.md) — Socket.IO endpoints in detail. -- [Configuration File](../reference/configuration-file.md) — `config.yml` schema. +- [Development Setup](development-setup.md): get a local env running. +- [API Reference](api-reference.md): what the backend exposes. +- [WebSockets](websockets.md): Socket.IO endpoints in detail. +- [Configuration File](../reference/configuration-file.md): `config.yml` schema. diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index 76633f51..8e71100d 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -7,17 +7,17 @@ description: How to contribute code, docs, translations, and bug reports to RomM Thanks for considering contributing. A few ground rules up front, then the mechanics. -!!! important "Big changes — open an issue first" +!!! important "Big changes: open an issue first" If you're planning a large feature or architectural change, open a GitHub issue **and** hop into the [Discord](https://discord.gg/romm) to discuss with maintainers before coding. A rejected 2,000-line PR is nobody's idea of fun. ## Code of Conduct The project follows the [Contributor Covenant](https://github.com/rommapp/romm/blob/master/CODE_OF_CONDUCT.md). By contributing, you agree to uphold it. -## AI assistance — please disclose +## AI assistance: please disclose !!! warning "Required disclosure" - If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR — including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count; anything more does. If PR responses are generated by an AI, disclose that too. + If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR, including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count; anything more does. If PR responses are generated by an AI, disclose that too. Example disclosures: @@ -27,7 +27,7 @@ Or more granularly: > I consulted ChatGPT to understand the codebase, but the solution was fully authored manually. -This isn't about gatekeeping AI contributions — it's about letting reviewers know how much scrutiny to apply. AI-assisted code often ranges from "fantastic" to "hallucinated nonsense"; we need to calibrate. +This isn't about gatekeeping AI contributions; it's about letting reviewers know how much scrutiny to apply. AI-assisted code often ranges from "fantastic" to "hallucinated nonsense"; we need to calibrate. Failing to disclose is rude to the humans reviewing your PR. Don't do it. @@ -35,9 +35,9 @@ Failing to disclose is rude to the humans reviewing your PR. Don't do it. ### Code -- **Features** — open an issue first for anything non-trivial. -- **Bug fixes** — happy to take these without pre-discussion if they're small and focused. -- **Refactors** — open an issue first. Refactors without a clear user-facing win are usually rejected. +- **Features:** open an issue first for anything non-trivial. +- **Bug fixes:** happy to take these without pre-discussion if they're small and focused. +- **Refactors:** open an issue first. Refactors without a clear user-facing win are usually rejected. ### Documentation @@ -45,7 +45,7 @@ You're on the docs site right now. If something's wrong, unclear, or missing: - PRs welcome against [rommapp/docs](https://github.com/rommapp/docs). - Small fixes (typos, broken links) don't need an issue first. -- Bigger changes (restructuring, adding a new section) — open an issue first or ping in Discord. +- Bigger changes (restructuring, adding a new section): open an issue first or ping in Discord. Building docs locally is covered in the docs repo's `CONTRIBUTING.md`. Short version: @@ -57,7 +57,7 @@ uv run mkdocs serve ### Translations -Create a new folder under `frontend/src/locales/` using the existing language files as a template, translate the strings, and open a PR. Partial translations are welcome — we'd rather have an 80%-translated locale than nothing. +Create a new folder under `frontend/src/locales/` using the existing language files as a template, translate the strings, and open a PR. Partial translations are welcome; we'd rather have an 80%-translated locale than nothing. See [Translations (i18n)](i18n.md) for the full translator workflow. @@ -68,7 +68,7 @@ See [Translations (i18n)](i18n.md) for the full translator workflow. - What happened, what you expected. - Exact reproduction steps. - RomM version and how you deployed it (Docker tag, Unraid, K8s, etc.). -- Relevant logs (`docker logs romm` — redact any secrets). +- Relevant logs (`docker logs romm`; redact any secrets). The bug report template prompts for all of this. @@ -83,7 +83,7 @@ The bug report template prompts for all of this. ```sh cd romm && git checkout master ``` -4. Set up your dev environment — [Development Setup](development-setup.md). +4. Set up your dev environment: [Development Setup](development-setup.md). 5. Create a branch: ```sh git checkout -b short-feature-name @@ -100,7 +100,7 @@ The bug report template prompts for all of this. ## PR guidelines -- **Lint clean.** `trunk check` must pass — CI will block otherwise. See [Development Setup → Linting](development-setup.md#linting). +- **Lint clean.** `trunk check` must pass; CI will block otherwise. See [Development Setup → Linting](development-setup.md#linting). - **Tests pass + new tests for new code.** `uv run pytest` must pass. New behaviour needs coverage. - **Docs updated** when behaviour changes. Even a one-line update to the relevant docs page is better than stale docs. - **Clear title + description.** "Fix bug" isn't a title. "Fix scan skipping multi-disc PS1 games when first disc is a .chd" is. @@ -118,7 +118,7 @@ Match the existing style. If you use VS Code (or a compatible editor), these ext ## Licensing -By contributing, you agree that your contributions are licensed under the project's [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE) — AGPL-3.0 for the core app. +By contributing, you agree that your contributions are licensed under the project's [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE): AGPL-3.0 for the core app. --- diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index 08967656..924e13fe 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -1,15 +1,15 @@ --- title: Development Setup -description: Run RomM locally for development — Docker, manual, tests, lint. +description: Run RomM locally for development: Docker, manual, tests, lint. --- # Development Setup -Two paths: **Docker** (recommended — matches production closely) or **manual** (edit Python on your host, faster iteration cycles). +Two paths: **Docker** (recommended, matches production closely) or **manual** (edit Python on your host, faster iteration cycles). If you're contributing, also read [Contributing](contributing.md). -## Option 1 — Docker +## Option 1: Docker Simplest. One command brings up the whole stack. @@ -47,9 +47,9 @@ docker compose build # --no-cache to force a clean rebuild docker compose up -d ``` -That's it. RomM is on `http://localhost:3000`. Source mounts are live — edit backend or frontend code and changes reflect on the next request (backend) or instantly (frontend HMR). +That's it. RomM is on `http://localhost:3000`. Source mounts are live: edit backend or frontend code and changes reflect on the next request (backend) or instantly (frontend HMR). -## Option 2 — Manual +## Option 2: Manual Faster iteration cycles than Docker if you're touching Python a lot. @@ -80,7 +80,7 @@ Adjust for your distro; the key libraries are the MariaDB connector and libpq fo #### RAHasher (optional) -Only needed if you're working on RetroAchievements hash calculation. **Not supported on macOS — skip.** +Only needed if you're working on RetroAchievements hash calculation. **Not supported on macOS; skip.** ```sh git clone --recursive https://github.com/RetroAchievements/RALibretro.git @@ -146,7 +146,7 @@ Frontend is on `http://localhost:3000`; it proxies API calls through to the back ## Linting -RomM uses [Trunk](https://trunk.io) as a meta-linter — it wraps ruff, prettier, eslint, markdownlint, and a few others under one config. +RomM uses [Trunk](https://trunk.io) as a meta-linter; it wraps ruff, prettier, eslint, markdownlint, and a few others under one config. ```sh curl https://get.trunk.io -fsSL | bash @@ -158,7 +158,7 @@ trunk check # report what it can't Trunk runs as a pre-commit hook automatically after install. Alternative install methods are in [the Trunk docs](https://docs.trunk.io/check/usage#install-the-cli). !!! warning "CI blocks un-linted PRs" - Trunk's check runs on every PR. If it fails, your PR can't merge — same rules as the maintainers. + Trunk's check runs on every PR. If it fails, your PR can't merge; same rules as the maintainers. ## Tests @@ -187,19 +187,19 @@ uv run pytest -k scan # match by name | URL | Purpose | | --- | --- | | `http://localhost:3000` | Main UI | -| `http://localhost:3000/api/docs` | Swagger UI — try endpoints live | +| `http://localhost:3000/api/docs` | Swagger UI: try endpoints live | | `http://localhost:3000/api/redoc` | ReDoc-rendered API reference | -| `http://localhost:3000/openapi.json` | OpenAPI spec — source of truth for client generators | +| `http://localhost:3000/openapi.json` | OpenAPI spec: source of truth for client generators | | `http://localhost:3000/api/heartbeat` | Health + config snapshot | ## Architecture at a glance If you're new to the codebase, read [Architecture](architecture.md) for a high-level walkthrough, then come back here. The short version: -- `backend/` — FastAPI, SQLAlchemy, Alembic, RQ workers. -- `frontend/` — Vue 3 + Vuetify + Pinia + Vite. Separate `/console` SPA for TV/gamepad mode. -- `docker/` — nginx config (with `mod_zip`), entrypoint scripts, multi-stage Dockerfiles. -- `examples/` — reference compose files. +- `backend/`: FastAPI, SQLAlchemy, Alembic, RQ workers. +- `frontend/`: Vue 3 + Vuetify + Pinia + Vite. Separate `/console` SPA for TV/gamepad mode. +- `docker/`: nginx config (with `mod_zip`), entrypoint scripts, multi-stage Dockerfiles. +- `examples/`: reference compose files. ## Getting stuck diff --git a/docs/developers/i18n.md b/docs/developers/i18n.md index 3cf014ce..5aaf8f61 100644 --- a/docs/developers/i18n.md +++ b/docs/developers/i18n.md @@ -13,7 +13,7 @@ This page is for **contributors** adding or improving translations. - **App UI** is translated. 19 locales currently ship. - **Docs** (what you're reading) are English-only in 5.0. -- **Game metadata** isn't translated by RomM — that comes from metadata providers (IGDB, ScreenScraper). Their localisation varies per title. +- **Game metadata** isn't translated by RomM; that comes from metadata providers (IGDB, ScreenScraper). Their localisation varies per title. ## Tech stack @@ -21,7 +21,7 @@ This page is for **contributors** adding or improving translations. - **File format**: JSON (one file per locale). - **Location**: `frontend/src/locales//`. -Each locale folder contains translation files. `en_US/` is the reference — always kept complete and current. Other locales may have partial coverage (missing keys fall back to `en_US`). +Each locale folder contains translation files. `en_US/` is the reference, always kept complete and current. Other locales may have partial coverage (missing keys fall back to `en_US`). ## Adding keys to an existing locale @@ -66,14 +66,14 @@ After your contribution: Scenario: the language you want isn't in the dropdown yet. -1. Pick your locale code. Use [BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) format — `de_DE`, `pt_BR`, `zh_TW`. The underscore convention matches existing folders. +1. Pick your locale code. Use [BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) format: `de_DE`, `pt_BR`, `zh_TW`. The underscore convention matches existing folders. 2. Copy `frontend/src/locales/en_US/` to `frontend/src/locales//`. -3. Translate the strings. Start with the most-visible ones (menu, navigation, home dashboard) — partial coverage is welcome. +3. Translate the strings. Start with the most-visible ones (menu, navigation, home dashboard); partial coverage is welcome. 4. Register the locale in the index file (filename varies; look for something like `frontend/src/locales/index.ts` that lists active locales). 5. Run `npm run dev` and verify your locale appears in the Language dropdown. 6. Open a PR. -**Partial translations are merged.** Don't hold out for 100% — English fallback handles gaps gracefully. A 60%-translated Arabic is better than no Arabic. +**Partial translations are merged.** Don't hold out for 100%; English fallback handles gaps gracefully. A 60%-translated Arabic is better than no Arabic. ## Style guidance @@ -87,7 +87,7 @@ Match the source tone: ### Consistency -Use the same word for the same concept throughout. If you translate "Collection" as one word at the top of a file, don't translate it differently three screens later. The `en_US` file has a glossary at the top for key terms — mirror that pattern in your locale. +Use the same word for the same concept throughout. If you translate "Collection" as one word at the top of a file, don't translate it differently three screens later. The `en_US` file has a glossary at the top for key terms; mirror that pattern in your locale. ### Interpolations @@ -118,13 +118,13 @@ Pipe-separated forms: zero | one | other. Not every language has three; `vue-i18 ### RTL languages -No RTL locales ship yet. The UI needs layout work before Arabic / Hebrew can present cleanly — if you want to contribute one, open a prep issue first so we can coordinate the layout fixes. +No RTL locales ship yet. The UI needs layout work before Arabic / Hebrew can present cleanly. If you want to contribute one, open a prep issue first so we can coordinate the layout fixes. ## Testing your translation 1. `cd frontend && npm install && npm run dev` 2. Open `http://localhost:3000`, pick your locale from Profile → User Interface → Language. -3. Click through every major screen — home dashboard, platform view, game detail, admin panel, settings. +3. Click through every major screen: home dashboard, platform view, game detail, admin panel, settings. Look for: @@ -134,17 +134,17 @@ Look for: ## Tooling -We don't use a translation-management platform (Weblate, Crowdin, etc.) in 5.0 — translations happen via PR. If interest grows, we may add one. Open an issue to discuss if you'd prefer it. +We don't use a translation-management platform (Weblate, Crowdin, etc.) in 5.0; translations happen via PR. If interest grows, we may add one. Open an issue to discuss if you'd prefer it. ## PR conventions - One PR per locale is ideal. - Title: `i18n(): `. E.g. `i18n(fr_FR): fill in missing Admin panel strings`. -- Include screenshots of the changed screens — reviewers can't read every locale. +- Include screenshots of the changed screens; reviewers can't read every locale. - Disclose AI assistance per [Contributing](contributing.md). ## See also -- [Languages](../using/languages.md) — the end-user view. -- [Contributing](contributing.md) — general contribution rules. -- [vue-i18n docs](https://vue-i18n.intlify.dev/) — upstream reference. +- [Languages](../using/languages.md): the end-user view. +- [Contributing](contributing.md): general contribution rules. +- [vue-i18n docs](https://vue-i18n.intlify.dev/): upstream reference. diff --git a/docs/developers/index.md b/docs/developers/index.md index 7b672b8c..028e7486 100644 --- a/docs/developers/index.md +++ b/docs/developers/index.md @@ -1,43 +1,43 @@ --- title: API & Development -description: Build on top of RomM — API reference, WebSockets, OpenAPI, local dev, contributing. +description: Build on top of RomM: API reference, WebSockets, OpenAPI, local dev, contributing. --- # API & Development -For anyone **building something on top of or inside of RomM** — third-party apps, scripts, contributions to the core, translations. +For anyone **building something on top of or inside of RomM**: third-party apps, scripts, contributions to the core, translations. Looking for end-user or operator content? See [Using RomM](../using/index.md) or [Administration](../administration/index.md). ## Working with the API -- **[API Reference](api-reference.md)** — every REST endpoint. OpenAPI-driven. -- **[API Authentication](api-authentication.md)** — all five auth modes (session, Basic, OAuth2, Client API Token, OIDC). -- **[Consuming OpenAPI](openapi.md)** — codegen, Postman imports, schema validation. -- **[WebSockets](websockets.md)** — Socket.IO endpoints for live updates and Netplay. +- **[API Reference](api-reference.md):** every REST endpoint. OpenAPI-driven. +- **[API Authentication](api-authentication.md):** all five auth modes (session, Basic, OAuth2, Client API Token, OIDC). +- **[Consuming OpenAPI](openapi.md):** codegen, Postman imports, schema validation. +- **[WebSockets](websockets.md):** Socket.IO endpoints for live updates and Netplay. ## Building companion apps -- **[Client API Tokens](../ecosystem/client-api-tokens.md)** — how companion apps authenticate, including the device-pairing flow. -- **[Device Sync Protocol](../ecosystem/device-sync-protocol.md)** — wire-level reference for save/state/play-session sync. -- **[Argosy](../ecosystem/argosy.md), [Grout](../ecosystem/grout.md)** — reference implementations. +- **[Client API Tokens](../ecosystem/client-api-tokens.md):** how companion apps authenticate, including the device-pairing flow. +- **[Device Sync Protocol](../ecosystem/device-sync-protocol.md):** wire-level reference for save/state/play-session sync. +- **[Argosy](../ecosystem/argosy.md), [Grout](../ecosystem/grout.md):** reference implementations. ## Contributing to RomM itself -- **[Development Setup](development-setup.md)** — get a local environment running. -- **[Architecture](architecture.md)** — high-level walkthrough of the codebase. -- **[Contributing](contributing.md)** — process, style, AI-assistance disclosure. -- **[Translations (i18n)](i18n.md)** — add or improve a locale. -- **[Releasing](releasing.md)** — maintainer-only; how releases are cut. +- **[Development Setup](development-setup.md):** get a local environment running. +- **[Architecture](architecture.md):** high-level walkthrough of the codebase. +- **[Contributing](contributing.md):** process, style, AI-assistance disclosure. +- **[Translations (i18n)](i18n.md):** add or improve a locale. +- **[Releasing](releasing.md):** maintainer-only; how releases are cut. ## Reference -- **[Environment Variables](../reference/environment-variables.md)** — every env var. -- **[Configuration File](../reference/configuration-file.md)** — `config.yml` schema. -- **[Scheduled Tasks](../reference/scheduled-tasks.md)** — background job reference. -- **[Exports](../reference/exports.md)** — gamelist.xml / Pegasus export formats. -- **[Feeds](../reference/feeds.md)** — every URL-feed endpoint (Tinfoil, pkgj, WebRcade, etc.). -- **[Glossary](../reference/glossary.md)** — canonical terminology. +- **[Environment Variables](../reference/environment-variables.md):** every env var. +- **[Configuration File](../reference/configuration-file.md):** `config.yml` schema. +- **[Scheduled Tasks](../reference/scheduled-tasks.md):** background job reference. +- **[Exports](../reference/exports.md):** gamelist.xml / Pegasus export formats. +- **[Feeds](../reference/feeds.md):** every URL-feed endpoint (Tinfoil, pkgj, WebRcade, etc.). +- **[Glossary](../reference/glossary.md):** canonical terminology. ## Quick orientation diff --git a/docs/developers/openapi.md b/docs/developers/openapi.md index baa2969b..6280aee4 100644 --- a/docs/developers/openapi.md +++ b/docs/developers/openapi.md @@ -5,7 +5,7 @@ description: Use RomM's OpenAPI spec for code generation, Postman imports, and A # Consuming OpenAPI -RomM ships its entire REST API as an OpenAPI 3.0 specification. That's the source of truth — every endpoint, every request/response schema, every parameter. Use it to generate clients, drive test suites, or import into tools. +RomM ships its entire REST API as an OpenAPI 3.0 specification. That's the source of truth: every endpoint, every request/response schema, every parameter. Use it to generate clients, drive test suites, or import into tools. ## Where to find the spec @@ -15,7 +15,7 @@ Every RomM instance serves: {romm_url}/openapi.json ``` -No authentication required — the spec itself is public, though calling most of the endpoints it describes requires auth. +No authentication required: the spec itself is public, though calling most of the endpoints it describes requires auth. Human-readable versions: @@ -56,21 +56,21 @@ npx @openapitools/openapi-generator-cli generate \ -o ./romm-client-ts ``` -Generated clients handle auth, request shaping, and response parsing. Drop in, import, call. Quality varies by generator target — Python and TypeScript are the best-tested. +Generated clients handle auth, request shaping, and response parsing. Drop in, import, call. Quality varies by generator target; Python and TypeScript are the best-tested. ### Tips - **Pin the spec**, don't fetch live. Builds should be reproducible. - **Regenerate on new RomM releases.** Breaking changes are rare but possible. -- **Patch if needed.** Generated clients sometimes need tweaks — upstream generator bugs, our spec's rough edges, etc. Keep a patch file. +- **Patch if needed.** Generated clients sometimes need tweaks (upstream generator bugs, our spec's rough edges, etc.). Keep a patch file. ## Postman / Insomnia / Bruno All three import OpenAPI directly: -- **Postman** — File → Import → paste `openapi.json` URL. -- **Insomnia** — Create → Import From → URL. -- **Bruno** — Import Collection → OpenAPI. +- **Postman:** File → Import → paste `openapi.json` URL. +- **Insomnia:** Create → Import From → URL. +- **Bruno:** Import Collection → OpenAPI. Useful for manual API exploration during development. @@ -78,15 +78,15 @@ Useful for manual API exploration during development. If you're building something that calls RomM, consider validating requests against the spec before sending. Schema-driven validation catches bugs early: -- **Python** — [`openapi-core`](https://github.com/python-openapi/openapi-core). -- **Node.js** — [`openapi-backend`](https://github.com/anttiviljami/openapi-backend) or `ajv`-based approaches. -- **Go** — [`kin-openapi`](https://github.com/getkin/kin-openapi). +- **Python:** [`openapi-core`](https://github.com/python-openapi/openapi-core). +- **Node.js:** [`openapi-backend`](https://github.com/anttiviljami/openapi-backend) or `ajv`-based approaches. +- **Go:** [`kin-openapi`](https://github.com/getkin/kin-openapi). ## Spec quirks A few known quirks to work around: -- **Some `additionalProperties` are loose.** RomM's spec lets some responses include fields not in the schema (debug hooks, feature-flag-gated fields). Don't treat the spec as an exact response guarantee — treat it as "everything here is always present; more may follow". +- **Some `additionalProperties` are loose.** RomM's spec lets some responses include fields not in the schema (debug hooks, feature-flag-gated fields). Don't treat the spec as an exact response guarantee; treat it as "everything here is always present; more may follow". - **Socket.IO isn't in the OpenAPI spec.** WebSocket endpoints are documented separately in [WebSockets](websockets.md). - **Pagination defaults vary per endpoint.** Some paginate, some don't. Check the spec per endpoint. @@ -96,6 +96,6 @@ Not currently in the spec. Event-driven integration is via [WebSockets](websocke ## See also -- [API Reference](api-reference.md) — the pre-rendered version of the spec for browsing. -- [API Authentication](api-authentication.md) — required auth for most endpoints. -- [OpenAPI Initiative](https://www.openapis.org/) — upstream specification. +- [API Reference](api-reference.md): the pre-rendered version of the spec for browsing. +- [API Authentication](api-authentication.md): required auth for most endpoints. +- [OpenAPI Initiative](https://www.openapis.org/): upstream specification. diff --git a/docs/developers/releasing.md b/docs/developers/releasing.md index 61be84af..05f06b33 100644 --- a/docs/developers/releasing.md +++ b/docs/developers/releasing.md @@ -5,23 +5,23 @@ description: How RomM releases are cut, published, and documented. Maintainer re # Releasing -Maintainer reference. If you're not cutting a RomM release, you don't need this page — see [Release Notes & Migration](../releases/index.md) for user-facing release info. +Maintainer reference. If you're not cutting a RomM release, you don't need this page. See [Release Notes & Migration](../releases/index.md) for user-facing release info. ## Release cadence -RomM releases on a loose cadence — not scheduled, driven by readiness: +RomM releases on a loose cadence, not scheduled, driven by readiness: -- **Patch (`5.0.1`, `5.0.2`)** — bug fixes. Cut as needed; typically 1-4 per month. -- **Minor (`5.1.0`, `5.2.0`)** — additive features. Cut when a cohesive batch of features is stable. -- **Major (`6.0.0`)** — breaking changes. Planned well in advance, announced in the Discord + on GitHub. +- **Patch (`5.0.1`, `5.0.2`):** bug fixes. Cut as needed; typically 1-4 per month. +- **Minor (`5.1.0`, `5.2.0`):** additive features. Cut when a cohesive batch of features is stable. +- **Major (`6.0.0`):** breaking changes. Planned well in advance, announced in the Discord + on GitHub. ## Version numbering [SemVer](https://semver.org/) for breaking-change semantics: -- **MAJOR** — backwards-incompatible schema change, env var rename, or API-contract break. -- **MINOR** — new feature, backwards-compatible. -- **PATCH** — bug fix only. +- **MAJOR:** backwards-incompatible schema change, env var rename, or API-contract break. +- **MINOR:** new feature, backwards-compatible. +- **PATCH:** bug fix only. Alembic migrations run on every startup; migrations are backwards-compatible within a major version. @@ -38,20 +38,20 @@ Alembic migrations run on every startup; migrations are backwards-compatible wit - `pyproject.toml` → `version = "X.Y.Z"`. - `frontend/package.json` → `"version": "X.Y.Z"`. -- Any hardcoded version strings (`backend/__init__.py`, etc.) — `rg '__version__'` or `rg '5\.0\.0'` to find them. +- Any hardcoded version strings (`backend/__init__.py`, etc.); `rg '__version__'` or `rg '5\.0\.0'` to find them. ### 3. Update `env.template` if needed If the release adds / renames / removes env vars, `env.template` is the canonical reference. Add/rename/remove lines with inline comments. Keep alphabetical order per section. -The docs site auto-generates [Environment Variables](../reference/environment-variables.md) from `env.template` on every release — skip this step and the docs drift. +The docs site auto-generates [Environment Variables](../reference/environment-variables.md) from `env.template` on every release; skip this step and the docs drift. ### 4. Update changelog -`CHANGELOG.md` at repo root (if present) — add a new section for the new version: +`CHANGELOG.md` at repo root (if present): add a new section for the new version: ```md -## 5.0.0 — 2026-04-18 +## 5.0.0 (2026-04-18) ### Breaking @@ -78,7 +78,7 @@ git tag -a 5.0.0 -m "Release 5.0.0" git push origin 5.0.0 ``` -The tag triggers the Docker build workflow — `rommapp/romm:5.0.0` (full) and `rommapp/romm:5.0.0-slim` are built and published to Docker Hub + GHCR. +The tag triggers the Docker build workflow: `rommapp/romm:5.0.0` (full) and `rommapp/romm:5.0.0-slim` are built and published to Docker Hub + GHCR. ### 6. Publish the GitHub Release @@ -110,9 +110,9 @@ docker push rommapp/romm:5 ## Announcements -- **Discord `#announcements`** — post a short summary with a link to the GitHub Release. -- **Reddit** (r/selfhosted, etc.) — optional, for major versions. -- **Docs site version switcher** — publish a new `mike`-managed version of the docs. See [Docs versioning](#docs-versioning). +- **Discord `#announcements`:** post a short summary with a link to the GitHub Release. +- **Reddit** (r/selfhosted, etc.): optional, for major versions. +- **Docs site version switcher:** publish a new `mike`-managed version of the docs. See [Docs versioning](#docs-versioning). ## Docs versioning @@ -140,7 +140,7 @@ If a regression ships in the release: ### Track issues -Post-release, expect a spike in issues. Triage Day-1 issues aggressively — breakage reports need immediate attention; nice-to-haves can wait. +Post-release, expect a spike in issues. Triage Day-1 issues aggressively: breakage reports need immediate attention; nice-to-haves can wait. ## Security releases @@ -162,6 +162,6 @@ For major versions: ## See also -- [Release Notes & Migration](../releases/index.md) — user-facing side. -- [Upgrading to 5.0](../releases/upgrading-to-5.0.md) — reference migration guide style. -- [Contributing](contributing.md) — general contribution process. +- [Release Notes & Migration](../releases/index.md): user-facing side. +- [Upgrading to 5.0](../releases/upgrading-to-5.0.md): reference migration guide style. +- [Contributing](contributing.md): general contribution process. diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md index b9b16ea2..bc5795a3 100644 --- a/docs/developers/websockets.md +++ b/docs/developers/websockets.md @@ -1,6 +1,6 @@ --- title: WebSockets -description: RomM's two Socket.IO endpoints — live updates and Netplay coordination. +description: RomM's two Socket.IO endpoints for live updates and Netplay coordination. --- # WebSockets @@ -9,8 +9,8 @@ RomM uses **Socket.IO** for real-time communication. Two endpoints: | Endpoint | Purpose | | --- | --- | -| `/ws/socket.io` | General live updates — scan progress, task status, task completion, admin notifications. | -| `/netplay/socket.io` | Netplay session coordination — room discovery, join/leave events, session lifecycle. | +| `/ws/socket.io` | General live updates: scan progress, task status, task completion, admin notifications. | +| `/netplay/socket.io` | Netplay session coordination: room discovery, join/leave events, session lifecycle. | Both are Redis-backed (via `socket.io-redis`) so multi-instance RomM deployments broadcast events to every replica. @@ -18,11 +18,11 @@ Both are Redis-backed (via `socket.io-redis`) so multi-instance RomM deployments RomM inherited Socket.IO from the Vue frontend, which uses `socket.io-client`. Sticking with Socket.IO avoids protocol drift, works in every browser, and handles reconnection + message framing for us. -If you're writing a non-browser client in a language that has a Socket.IO library (Python's `python-socketio`, Go's `go-socket.io`, etc.), the protocol is straightforward. Raw WebSocket without Socket.IO framing **will not** work — Socket.IO adds its own handshake and message envelope. +If you're writing a non-browser client in a language that has a Socket.IO library (Python's `python-socketio`, Go's `go-socket.io`, etc.), the protocol is straightforward. Raw WebSocket without Socket.IO framing **will not** work; Socket.IO adds its own handshake and message envelope. ## Authentication -Socket.IO connections inherit the HTTP session — if your `Cookie` header carries a RomM session cookie, the WS connection is authenticated as that user. Programmatic clients should pass the session or token via the handshake `auth` field: +Socket.IO connections inherit the HTTP session: if your `Cookie` header carries a RomM session cookie, the WS connection is authenticated as that user. Programmatic clients should pass the session or token via the handshake `auth` field: ```javascript const socket = io("https://romm.example.com", { @@ -35,7 +35,7 @@ const socket = io("https://romm.example.com", { If auth fails, the connection is closed with an error event. -## `/ws/socket.io` — general events +## `/ws/socket.io`: general events ### Namespaces @@ -58,14 +58,14 @@ Default namespace. No sub-namespacing in 5.0. ### Client → server events -No state changes via WebSocket — RomM's design is "REST for writes, Socket.IO for reads". A client can: +No state changes via WebSocket; RomM's design is "REST for writes, Socket.IO for reads". A client can: - Emit `subscribe:scan` with a scan ID to join that scan's broadcast group. - Emit `unsubscribe:scan` to leave. -Actual scan / task / ROM operations happen via the REST API — see [API Reference](api-reference.md). +Actual scan / task / ROM operations happen via the REST API; see [API Reference](api-reference.md). -## `/netplay/socket.io` — Netplay coordination +## `/netplay/socket.io`: Netplay coordination Separate endpoint for Netplay rooms. Used by EmulatorJS's Netplay logic; rarely touched directly. @@ -89,18 +89,18 @@ Plus WebRTC signalling events (`offer`, `answer`, `ice_candidate`) that shuttle ## Redis backend (multi-instance) -When you run multiple RomM replicas behind a load balancer, a WS-originated event on one replica needs to reach a client connected to another replica. `socket.io-redis` handles that — events are published to a Redis pub/sub channel, every replica subscribes, every replica delivers to its local sticky-session clients. +When you run multiple RomM replicas behind a load balancer, a WS-originated event on one replica needs to reach a client connected to another replica. `socket.io-redis` handles that: events are published to a Redis pub/sub channel, every replica subscribes, every replica delivers to its local sticky-session clients. Required config when running multi-replica: - `REDIS_HOST` + `REDIS_PORT` shared across replicas. - Load balancer with **sticky sessions** (client IP or cookie hash). -Without sticky sessions, Socket.IO's handshake polling phase can bounce between replicas and fail — see [Socket.IO multi-server docs](https://socket.io/docs/v4/using-multiple-nodes/) for context. +Without sticky sessions, Socket.IO's handshake polling phase can bounce between replicas and fail. See [Socket.IO multi-server docs](https://socket.io/docs/v4/using-multiple-nodes/) for context. ## Reverse-proxy requirements -Every reverse-proxy setup must forward the WebSocket upgrade. See [Reverse Proxy](../install/reverse-proxy.md) — all recipes there keep WebSockets on. +Every reverse-proxy setup must forward the WebSocket upgrade. See [Reverse Proxy](../install/reverse-proxy.md); all recipes there keep WebSockets on. Common breakages: @@ -110,9 +110,10 @@ Common breakages: Symptom of a broken WS: HTTP 400 responses on the upgrade, and the browser console full of `WebSocket connection failed`. See [Authentication Troubleshooting → WebSockets](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). + ## Writing a client in Python -Simple example — tail scan logs: +Simple example, tail scan logs: ```python import socketio @@ -137,12 +138,12 @@ sio.wait() ## Limitations -- **Not every backend action emits a WS event** — only the ones listed above. If you need a specific event, open an issue. -- **No room-based user presence yet** — "who else is online" isn't exposed. -- **Netplay WebRTC is peer-to-peer after initial handshake** — RomM only brokers; the actual gameplay data never touches RomM's servers. +- **Not every backend action emits a WS event.** Only the ones listed above. If you need a specific event, open an issue. +- **No room-based user presence yet.** "Who else is online" isn't exposed. +- **Netplay WebRTC is peer-to-peer after initial handshake.** RomM only brokers; the actual gameplay data never touches RomM's servers. ## See also -- [API Authentication](api-authentication.md) — general auth primer. -- [Reverse Proxy](../install/reverse-proxy.md) — every recipe needs WebSocket passthrough. -- [Netplay](../using/netplay.md) — end-user-facing side of the `/netplay` endpoint. +- [API Authentication](api-authentication.md): general auth primer. +- [Reverse Proxy](../install/reverse-proxy.md): every recipe needs WebSocket passthrough. +- [Netplay](../using/netplay.md): end-user-facing side of the `/netplay` endpoint. diff --git a/docs/ecosystem/argosy.md b/docs/ecosystem/argosy.md index 195fa1b8..b1b81b74 100644 --- a/docs/ecosystem/argosy.md +++ b/docs/ecosystem/argosy.md @@ -1,6 +1,6 @@ --- title: Argosy Launcher -description: Official Android launcher for RomM — browse and launch your library on mobile. +description: Official Android launcher for RomM, browse and launch your library on mobile. --- # Argosy Launcher @@ -15,7 +15,7 @@ description: Official Android launcher for RomM — browse and launch your libra ## What it does - Browses your full RomM library from an Android device. -- Handles authentication via [Client API Tokens](client-api-tokens.md) — paired over a short code so you don't type a 44-character string. +- Handles authentication via [Client API Tokens](client-api-tokens.md), paired over a short code so you don't type a 44-character string. - Downloads ROMs on demand to your device's storage. - Launches RetroArch (or another configured emulator) with the downloaded ROM. - Syncs saves / states back to RomM when you finish a session (optional). @@ -36,9 +36,9 @@ Not currently on either. APK sideloading is the path for now. 1. Launch Argosy. 2. Enter your RomM URL (e.g. `https://romm.example.com`). Needs HTTPS in production. 3. Choose **Pair Device**. -4. Argosy shows a pairing URL or QR code — open it on a device that's already signed into RomM. +4. Argosy shows a pairing URL or QR code: open it on a device that's already signed into RomM. 5. On that device: RomM shows a confirmation dialog. Enter the 8-digit code Argosy displayed. -6. Accept — Argosy receives a Client API Token bound to your RomM account. +6. Accept. Argosy receives a Client API Token bound to your RomM account. From here, Argosy lists your library. Full pairing-flow details in [Client API Tokens](client-api-tokens.md). @@ -65,8 +65,8 @@ RetroArch users: Argosy can auto-detect installed RetroArch cores and map platfo Argosy → Settings → Sync. Two modes: -- **On session end** — uploads saves back to RomM when you exit the emulator. -- **Manual** — you tap Upload when you want. +- **On session end**: uploads saves back to RomM when you exit the emulator. +- **Manual**: you tap Upload when you want. Saves show up in RomM's **Game Data** tab on the game, same as in-browser saves. See [Saves & States](../using/saves-and-states.md). @@ -74,23 +74,23 @@ Saves show up in RomM's **Game Data** tab on the game, same as in-browser saves. Argosy needs: -- **Storage** — to save downloaded ROMs. -- **Network** — to talk to your RomM instance. -- **Optional: Notifications** — download-complete and sync-complete pings. +- **Storage**: to save downloaded ROMs. +- **Network**: to talk to your RomM instance. +- **Optional: Notifications**: download-complete and sync-complete pings. No other permissions. The app doesn't request contacts, camera, location, or anything else. ## Troubleshooting -- **Can't connect to RomM** — check the URL (including `https://`), and that the RomM instance is reachable from your mobile network. Cellular might be blocked; try Wi-Fi first. -- **Token invalid** — pair again. Tokens can expire or be revoked on the RomM side. -- **Emulator won't launch** — make sure the emulator app is installed and Argosy has permission to open it. Some emulators require an intent-filter setup. -- **Downloads fail partway** — usually network; Argosy resumes on retry. +- **Can't connect to RomM.** Check the URL (including `https://`), and that the RomM instance is reachable from your mobile network. Cellular might be blocked; try Wi-Fi first. +- **Token invalid.** Pair again. Tokens can expire or be revoked on the RomM side. +- **Emulator won't launch.** Make sure the emulator app is installed and Argosy has permission to open it. Some emulators require an intent-filter setup. +- **Downloads fail partway.** Usually network; Argosy resumes on retry. Full sync-specific debugging in [Device Sync Troubleshooting](../troubleshooting/sync.md). ## See also -- [Client API Tokens](client-api-tokens.md) — the auth + pairing flow Argosy uses. -- [Device Sync Protocol](device-sync-protocol.md) — how saves sync. -- [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher) — source, issues, releases. +- [Client API Tokens](client-api-tokens.md): the auth + pairing flow Argosy uses. +- [Device Sync Protocol](device-sync-protocol.md): how saves sync. +- [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher): source, issues, releases. diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index 66da4b43..edac9c21 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -7,7 +7,7 @@ description: Long-lived bearer tokens for companion apps, plus the device-pairin A **Client API Token** is a long-lived credential that a companion app (or script, or CI job) uses to authenticate against RomM on behalf of a specific user. Think "personal access token" on GitHub. -Tokens are **per-user** and **per-scope-subset** — a token can hold any subset of the owning user's scopes, scoped narrower than the user's role. You get up to **25 active tokens per user**. +Tokens are **per-user** and **per-scope-subset**: a token can hold any subset of the owning user's scopes, scoped narrower than the user's role. You get up to **25 active tokens per user**. ## Why not just store a password? @@ -34,11 +34,11 @@ Authorization: Bearer rmm_abcdef... From the RomM UI: **Profile → Client API Tokens → + New Token**. -- **Name** — descriptive (e.g. "Grout on RG35XX"). -- **Scopes** — tick which scopes to include. Default: read-only. Think about it; don't give every token `users.write`. -- **Expiry** — optional; blank = never expires until revoked. +- **Name**: descriptive (e.g. "Grout on RG35XX"). +- **Scopes**: tick which scopes to include. Default: read-only. Think about it; don't give every token `users.write`. +- **Expiry**: optional; blank = never expires until revoked. -The token is shown **exactly once**. Copy it now. If you lose it, revoke and regenerate — you can't get it back. +The token is shown **exactly once**. Copy it now. If you lose it, revoke and regenerate; you can't get it back. ## Device pairing (short-code flow) @@ -72,16 +72,16 @@ Typing a 44-character token into a handheld thumbstick isn't realistic. Instead: ### Timing - Pairing codes are valid for **5 minutes** after creation. -- Single-use — once a device exchanges the code, it's invalid for anyone else. +- Single-use: once a device exchanges the code, it's invalid for anyone else. - Re-create if the user doesn't complete within the window. ### Who generates the code -The user who owns the token, from a device already signed into RomM (web UI, usually). The handheld / companion device then enters the code. No way for a device to generate a code on its own — that would defeat the pairing. +The user who owns the token, from a device already signed into RomM (web UI, usually). The handheld / companion device then enters the code. No way for a device to generate a code on its own; that would defeat the pairing. ### What "pairing" gives you -A Client API Token bound to the device's installation. The companion app stores the token and uses it on every subsequent API call. From RomM's side, it looks like any other token — no special treatment beyond the fact that pairing is how the token got there. +A Client API Token bound to the device's installation. The companion app stores the token and uses it on every subsequent API call. From RomM's side, it looks like any other token: no special treatment beyond the fact that pairing is how the token got there. ## API reference for implementers @@ -149,9 +149,9 @@ A token can only hold scopes the owning user *also* holds. If the user is an Edi Useful narrow scope-sets: -- **Library-only read**: `roms.read`, `platforms.read`, `collections.read`, `devices.read` — e.g. for a browse-only app. +- **Library-only read**: `roms.read`, `platforms.read`, `collections.read`, `devices.read` (e.g. for a browse-only app). - **Read + sync saves**: add `assets.read`, `assets.write`, `me.read`, `me.write`, `devices.write`. -- **Playnite / external launcher**: `roms.read`, `platforms.read`, `collections.read` — it only needs to browse and download. +- **Playnite / external launcher**: `roms.read`, `platforms.read`, `collections.read` (it only needs to browse and download). - **Grout / handheld companion**: library-read + assets read/write + device management. The UI's scope-selection step defaults to read-only. Only tick write scopes you actually need. @@ -161,7 +161,7 @@ The UI's scope-selection step defaults to read-only. Only tick write scopes you If the owning user's role drops below what the token needs: - Token continues to exist but fails at request time with **403 Forbidden**. -- RomM doesn't automatically revoke it — that's the user's decision. +- RomM doesn't automatically revoke it; that's the user's decision. If the user is deleted, all their tokens are revoked immediately. @@ -174,6 +174,6 @@ If the user is deleted, all their tokens are revoked immediately. ## See also -- [Device Sync Protocol](device-sync-protocol.md) — how the synced content flows after pairing. -- [API Authentication](../developers/api-authentication.md) — all RomM auth modes side-by-side. -- [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix) — the 19-scope taxonomy. +- [Device Sync Protocol](device-sync-protocol.md): how the synced content flows after pairing. +- [API Authentication](../developers/api-authentication.md): all RomM auth modes side-by-side. +- [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix): the 19-scope taxonomy. diff --git a/docs/ecosystem/community-apps.md b/docs/ecosystem/community-apps.md index dd2c795f..f44209df 100644 --- a/docs/ecosystem/community-apps.md +++ b/docs/ecosystem/community-apps.md @@ -1,11 +1,11 @@ --- title: Community Apps -description: Third-party companion apps for RomM — maintained by the community, not the RomM team. +description: Third-party companion apps for RomM, maintained by the community, not the RomM team. --- # Community Apps -Apps listed here are **community-maintained**. The RomM team doesn't build or officially support them — the authors do. Support is via the individual app's issue tracker and the RomM Discord. +Apps listed here are **community-maintained**. The RomM team doesn't build or officially support them; the authors do. Support is via the individual app's issue tracker and the RomM Discord. First-party alternatives (built by the RomM team): @@ -78,7 +78,7 @@ SteamOS downloader/syncer for Steam Deck. ### SwitchRomM -Homebrew NRO app for Nintendo Switch — pull ROMs from RomM over Wi-Fi. +Homebrew NRO app for Nintendo Switch; pull ROMs from RomM over Wi-Fi. - **Author:** [@Shalasere](https://github.com/Shalasere) - **Platform:** Nintendo Switch (homebrew) @@ -88,7 +88,7 @@ Homebrew NRO app for Nintendo Switch — pull ROMs from RomM over Wi-Fi. ### romm-comm -Discord bot for interacting with RomM from a Discord server — query library, post stats, request games. +Discord bot for interacting with RomM from a Discord server: query library, post stats, request games. - **Author:** [@idio-sync](https://github.com/idio-sync) - **Platform:** Discord bot @@ -110,10 +110,10 @@ Push a Syncthing-managed library to RomM automatically. - **Platform:** Wherever Syncthing runs - **Status:** Active -## "Community-maintained" — what it means +## "Community-maintained": what it means - **The RomM team doesn't build these.** We won't fix bugs, ship features, or respond to support tickets for community apps. -- **Support through the app author.** Each project has its own issue tracker — use it. +- **Support through the app author.** Each project has its own issue tracker; use it. - **Install at your own risk.** We don't code-review community apps or vouch for their security posture. - **Token safety.** These apps use [Client API Tokens](client-api-tokens.md) the same way first-party apps do. Scope tokens narrowly and revoke if an app misbehaves. @@ -131,4 +131,4 @@ We'll merge if the project's real, has a working repo, and isn't obviously broke ## Reporting an abandoned app -If an app here hasn't been updated in >12 months and the maintainer isn't responsive, let us know — either a PR marking it as Abandoned, or a Discord message in `#community-projects`. We flag abandoned projects so users know not to rely on them. +If an app here hasn't been updated in >12 months and the maintainer isn't responsive, let us know: either a PR marking it as Abandoned, or a Discord message in `#community-projects`. We flag abandoned projects so users know not to rely on them. diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md index 555d4ab2..fd37bb3a 100644 --- a/docs/ecosystem/device-sync-protocol.md +++ b/docs/ecosystem/device-sync-protocol.md @@ -1,6 +1,6 @@ --- title: Device Sync Protocol -description: Wire-level reference for RomM's save/state/play-session sync protocol — for companion-app developers. +description: Wire-level reference for RomM's save/state/play-session sync protocol, for companion-app developers. --- # Device Sync Protocol @@ -11,9 +11,9 @@ End users: this is under the hood. You don't need to read it. See [Saves & State ## The three primitives -1. **Devices** — registered endpoints. Each device has a name, hostname, and links to a specific user's [Client API Token](client-api-tokens.md). -2. **Sync sessions** — an atomic bidirectional sync run. One session handles "pull anything new from server" + "push anything new from device" + "reconcile conflicts" + "ingest play sessions". -3. **Play sessions** — per-session records of ROM playtime. Independent of sync but commonly posted in batch at the end of a sync run. +1. **Devices**: registered endpoints. Each device has a name, hostname, and links to a specific user's [Client API Token](client-api-tokens.md). +2. **Sync sessions**: an atomic bidirectional sync run. One session handles "pull anything new from server" + "push anything new from device" + "reconcile conflicts" + "ingest play sessions". +3. **Play sessions**: per-session records of ROM playtime. Independent of sync but commonly posted in batch at the end of a sync run. ## Authentication @@ -101,7 +101,7 @@ Content-Type: application/json } ``` -The device reports every save/state it knows about for each ROM — filename, last-modified time, and hash. +The device reports every save/state it knows about for each ROM: filename, last-modified time, and hash. ### Response @@ -145,13 +145,13 @@ RomM returns a set of **operations** the device should execute: ### Operation types -- **`upload`** — device pushes the file to the server. -- **`download`** — device pulls the file from the server. -- **`conflict`** — both sides have newer versions. Resolution strategy: - - `keep_both` — rename and keep both copies. - - `server_wins` / `device_wins` — overwrite the other. +- **`upload`**: device pushes the file to the server. +- **`download`**: device pulls the file from the server. +- **`conflict`**: both sides have newer versions. Resolution strategy: + - `keep_both`: rename and keep both copies. + - `server_wins` / `device_wins`: overwrite the other. - Default is `keep_both`. -- **`noop`** — nothing to do; hashes match. +- **`noop`**: nothing to do; hashes match. ### Execution @@ -230,7 +230,7 @@ Companion apps generally prefer the API model. SSH-based sync exists mostly for Not strict in 5.0. Reasonable rule of thumb: - Sync once per session (not every save). -- Large bursts (initial sync of a full library) are fine — RomM handles them. +- Large bursts (initial sync of a full library) are fine; RomM handles them. - Don't poll `/api/sync/negotiate` in a tight loop; it's expensive server-side. ## Event notifications @@ -239,8 +239,8 @@ Currently polling-only. Companion apps check `/api/sync/negotiate` periodically ## See also -- [Client API Tokens](client-api-tokens.md) — auth + pairing. -- [API Authentication](../developers/api-authentication.md) — general auth primer. -- [API Reference](../developers/api-reference.md) — full endpoint catalogue. -- [SSH Sync](../administration/ssh-sync.md) — alternative transport for handhelds. -- [Argosy](argosy.md), [Grout](grout.md) — reference client implementations. +- [Client API Tokens](client-api-tokens.md): auth + pairing. +- [API Authentication](../developers/api-authentication.md): general auth primer. +- [API Reference](../developers/api-reference.md): full endpoint catalogue. +- [SSH Sync](../administration/ssh-sync.md): alternative transport for handhelds. +- [Argosy](argosy.md), [Grout](grout.md): reference client implementations. diff --git a/docs/ecosystem/fpkgi.md b/docs/ecosystem/fpkgi.md index 2cb77c77..bb539c3a 100644 --- a/docs/ecosystem/fpkgi.md +++ b/docs/ecosystem/fpkgi.md @@ -7,13 +7,13 @@ description: Install PS4 / PS5 packages from your RomM library via fpkgi homebre [fpkgi](https://github.com/CyberYoshi64/fpkgi) is PS4 / PS5 homebrew for installing `.pkg` packages from custom URL feeds. RomM exposes fpkgi-compatible feeds for its PS4 and PS5 libraries. -New in RomM 5.0 — earlier versions didn't have fpkgi feeds. +New in RomM 5.0; earlier versions didn't have fpkgi feeds. ## Prerequisites -- **PS4 or PS5** with fpkgi installed (requires CFW / jailbreak — setup is out of scope here). -- **RomM reachable from the console over Wi-Fi** — LAN simplest. -- Games stored as `.pkg` files — fpkgi, like pkgj, only handles the Sony installer format. +- **PS4 or PS5** with fpkgi installed (requires CFW / jailbreak; setup is out of scope here). +- **RomM reachable from the console over Wi-Fi.** LAN simplest. +- Games stored as `.pkg` files. fpkgi, like pkgj, only handles the Sony installer format. ## Feed URL @@ -32,7 +32,7 @@ Example: http://192.168.1.100:3000/api/feeds/fpkgi/ps4 ``` -The feed returns JSON in the fpkgi-expected schema — titles, title IDs, content types, URLs back to RomM for the actual downloads. +The feed returns JSON in the fpkgi-expected schema: titles, title IDs, content types, URLs back to RomM for the actual downloads. ## Configuring fpkgi @@ -42,7 +42,7 @@ Exact steps depend on the fpkgi version, but the gist: 2. Restart fpkgi. 3. The RomM library appears in fpkgi's browse view. -Consult [fpkgi's README](https://github.com/CyberYoshi64/fpkgi) for the current config-file location and format — the project moves faster than these docs. +Consult [fpkgi's README](https://github.com/CyberYoshi64/fpkgi) for the current config-file location and format; the project moves faster than these docs. ## Authentication @@ -51,18 +51,18 @@ The `/api/feeds/fpkgi/` endpoints support basic auth the same way `/api/feeds/pk - Set basic-auth credentials in fpkgi's config if it supports them, OR - Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` on the RomM server (see the [Tinfoil](tinfoil.md) caveat about public exposure). -## File format — must be `.pkg` +## File format: must be `.pkg` -PS4 `.pkg` files specifically — not `.iso`, not compressed. RomM filters to `.pkg` when building the feed. Any other file types are invisible to fpkgi. +PS4 `.pkg` files specifically, not `.iso`, not compressed. RomM filters to `.pkg` when building the feed. Any other file types are invisible to fpkgi. ## Troubleshooting -- **Feed is empty** — no `.pkg` files on the `ps4` / `ps5` platform. Check your library. -- **Downloads fail with 401** — auth config mismatch. See Authentication section above. -- **Downloads succeed but install fails** — `.pkg` is for a different firmware version. Not a RomM problem. +- **Feed is empty.** No `.pkg` files on the `ps4` / `ps5` platform. Check your library. +- **Downloads fail with 401.** Auth config mismatch. See Authentication section above. +- **Downloads succeed but install fails.** `.pkg` is for a different firmware version. Not a RomM problem. ## See also -- [Feeds reference](../reference/feeds.md) — all feed endpoints. -- [pkgj](pkgj.md) — PS Vita / PSP equivalent. -- [fpkgi upstream](https://github.com/CyberYoshi64/fpkgi) — installer homebrew. +- [Feeds reference](../reference/feeds.md): all feed endpoints. +- [pkgj](pkgj.md): PS Vita / PSP equivalent. +- [fpkgi upstream](https://github.com/CyberYoshi64/fpkgi): installer homebrew. diff --git a/docs/ecosystem/grout.md b/docs/ecosystem/grout.md index 3109a34d..faa6abf4 100644 --- a/docs/ecosystem/grout.md +++ b/docs/ecosystem/grout.md @@ -1,11 +1,11 @@ --- title: Grout -description: Official Linux handheld companion for muOS and NextUI — sync ROMs and saves from RomM. +description: Official Linux handheld companion for muOS and NextUI, sync ROMs and saves from RomM. --- # Grout -**Grout** is RomM's first-party companion for Linux-based handhelds — specifically [muOS](https://muos.dev) and [NextUI](https://github.com/mattsays/nextui). Syncs ROMs, saves, and states bidirectionally with your RomM instance over Wi-Fi. +**Grout** is RomM's first-party companion for Linux-based handhelds, specifically [muOS](https://muos.dev) and [NextUI](https://github.com/mattsays/nextui). Syncs ROMs, saves, and states bidirectionally with your RomM instance over Wi-Fi. - **Repo:** [rommapp/grout](https://github.com/rommapp/grout) - **Language:** Go @@ -17,13 +17,13 @@ description: Official Linux handheld companion for muOS and NextUI — sync ROMs - Connects to your RomM instance using a [Client API Token](client-api-tokens.md). - **Pulls** ROMs from RomM to the handheld's SD card, organised into muOS / NextUI's expected folder layout. - **Pushes** saves and states back to RomM when you finish a session. -- **Schedules** sync runs — on idle, on session end, or on a cron. +- **Schedules** sync runs: on idle, on session end, or on a cron. - Works fully offline between syncs; the handheld doesn't need RomM to play. ## Why Grout (and not Argosy)? - **[Argosy](argosy.md)** is Android-native. Good for Android handhelds (Retroid Pocket running Android, phones). -- **Grout** is for non-Android Linux handhelds — muOS and NextUI are Linux, not Android. Argosy's APK won't install. +- **Grout** is for non-Android Linux handhelds. muOS and NextUI are Linux, not Android. Argosy's APK won't install. Same underlying protocol; different client for different OS. @@ -46,7 +46,7 @@ Same underlying protocol; different client for different OS. 1. Launch Grout on the handheld. 2. Enter your RomM URL. -3. **Pair Device** — Grout displays an 8-digit code. +3. **Pair Device**: Grout displays an 8-digit code. 4. On any device already signed into RomM, navigate to **Profile → Client API Tokens → + New Token** → create a new token → **Pair Device** → enter the code shown on Grout. 5. Grout receives the token, confirms pairing, and fetches your library metadata. @@ -62,49 +62,49 @@ Grout's main view lists platforms. Select one to see available ROMs. Metadata (t Select a ROM → **Download**. Grout pulls the file to the expected location on the SD card, so muOS / NextUI's native launcher sees it on next refresh. -Bulk select for multi-file downloads — useful for pulling a whole collection at once. +Bulk select for multi-file downloads, useful for pulling a whole collection at once. ### Sync cadence Grout → Settings → Sync: -- **Pull** cadence — how often to check RomM for new ROMs (default: manual; can set to every N minutes when on Wi-Fi). -- **Push** cadence — how often to upload saves (default: on session end). -- **Full sync** — manual; triggers a full bidirectional sync right now. +- **Pull** cadence: how often to check RomM for new ROMs (default: manual; can set to every N minutes when on Wi-Fi). +- **Push** cadence: how often to upload saves (default: on session end). +- **Full sync**: manual; triggers a full bidirectional sync right now. ### What pushes back to RomM -- **Save files** — once a session ends, Grout uploads any changed saves to RomM. -- **Save states** — same, if enabled in settings. -- **Play session records** — start/end times for the **Continue Playing** ribbon and per-ROM playtime on RomM. +- **Save files.** Once a session ends, Grout uploads any changed saves to RomM. +- **Save states.** Same, if enabled in settings. +- **Play session records.** Start/end times for the **Continue Playing** ribbon and per-ROM playtime on RomM. ## Permissions and access Grout uses your Client API Token for all API calls. Token scopes: -- `roms.read`, `platforms.read`, `collections.read` — to browse. -- `assets.read`, `assets.write` — to sync saves. -- `devices.read`, `devices.write` — to register as a device. -- `firmware.read` — if you're syncing firmware too. +- `roms.read`, `platforms.read`, `collections.read`: to browse. +- `assets.read`, `assets.write`: to sync saves. +- `devices.read`, `devices.write`: to register as a device. +- `firmware.read`: if you're syncing firmware too. -Scope the token narrowly when creating — default scopes are fine for most users, but an admin shouldn't hand Grout `users.write` just because the token-creation page offers it. +Scope the token narrowly when creating: default scopes are fine for most users, but an admin shouldn't hand Grout `users.write` just because the token-creation page offers it. ## SSH sync (operator-side) -If you want Grout to pull from RomM over SSH rather than HTTPS — e.g. on a trusted LAN with no reverse proxy — see [SSH Sync](../administration/ssh-sync.md) for the server-side config. Grout supports both modes, selectable in Settings → Connection. +If you want Grout to pull from RomM over SSH rather than HTTPS (e.g. on a trusted LAN with no reverse proxy) see [SSH Sync](../administration/ssh-sync.md) for the server-side config. Grout supports both modes, selectable in Settings → Connection. ## Troubleshooting -- **Can't see the handheld's Wi-Fi on RomM's network** — make sure both are on the same SSID / VLAN. -- **Token invalid** — re-pair; token was revoked or expired. -- **Saves aren't syncing** — check the sync cadence is set to something other than "never", and that the handheld actually has network during the sync window. -- **"Device not registered"** — the pairing step wasn't completed. Re-run pairing from scratch. +- **Can't see the handheld's Wi-Fi on RomM's network.** Make sure both are on the same SSID / VLAN. +- **Token invalid.** Re-pair; token was revoked or expired. +- **Saves aren't syncing.** Check the sync cadence is set to something other than "never", and that the handheld actually has network during the sync window. +- **"Device not registered".** The pairing step wasn't completed. Re-run pairing from scratch. More in [Device Sync Troubleshooting](../troubleshooting/sync.md). ## See also -- [Client API Tokens](client-api-tokens.md) — token and pairing flow reference. -- [Device Sync Protocol](device-sync-protocol.md) — wire-level protocol. -- [SSH Sync](../administration/ssh-sync.md) — operator-side SSH config. -- [rommapp/grout](https://github.com/rommapp/grout) — source, issues, releases. +- [Client API Tokens](client-api-tokens.md): token and pairing flow reference. +- [Device Sync Protocol](device-sync-protocol.md): wire-level protocol. +- [SSH Sync](../administration/ssh-sync.md): operator-side SSH config. +- [rommapp/grout](https://github.com/rommapp/grout): source, issues, releases. diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md index 47a5892d..ca6edca2 100644 --- a/docs/ecosystem/igir.md +++ b/docs/ecosystem/igir.md @@ -5,22 +5,22 @@ description: Clean up and normalise your ROM collection with Igir before importi # Igir Collection Manager -[Igir](https://igir.io/) is a zero-setup ROM collection manager — sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se; more a pre-processing tool. Useful for cleaning up a library *before* importing into RomM, so RomM's scans have a better-named, better-organised starting point. +[Igir](https://igir.io/) is a zero-setup ROM collection manager: sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se; more a pre-processing tool. Useful for cleaning up a library *before* importing into RomM, so RomM's scans have a better-named, better-organised starting point. **This is not an official RomM app.** Igir is a separate community project. We document integration here because it's a common workflow and produces a RomM-compatible layout directly. ## When you'd use Igir -- You have a messy collection — inconsistent naming, mixed formats, dumps from multiple sources. +- You have a messy collection: inconsistent naming, mixed formats, dumps from multiple sources. - You want to **match against No-Intro / Redump DAT files** to verify authenticity and standardise names. -- You want to **filter** — only retail releases, strip out hacks, keep only one region, etc. +- You want to **filter**: only retail releases, strip out hacks, keep only one region, etc. - You want to move / rename files to RomM's expected platform folder layout. If your library is already clean, skip Igir. RomM's scans handle naming variations gracefully. ## Directory setup -Igir works on a copy of your ROMs — never in place — to let you iterate on its config without risking the originals. +Igir works on a copy of your ROMs (never in place) to let you iterate on its config without risking the originals. ```text . @@ -40,8 +40,8 @@ cp -r roms/ roms-unverified/ DAT files are hash-referenced catalogues Igir matches against. -- **Cartridge systems:** [No-Intro daily](https://datomatic.no-intro.org/index.php?page=download&op=daily) — full DAT compilation. -- **Optical systems (PS1, Saturn, etc.):** [Redump](http://redump.org/downloads/) — per-platform DAT files. +- **Cartridge systems:** [No-Intro daily](https://datomatic.no-intro.org/index.php?page=download&op=daily), full DAT compilation. +- **Optical systems (PS1, Saturn, etc.):** [Redump](http://redump.org/downloads/), per-platform DAT files. Drop the DAT files into `dats/`. You can use a subset if you only care about specific platforms. @@ -81,12 +81,12 @@ chmod +x igir-romm-cleanup.sh ### What it does -- `move extract` — extract archives and move the results. -- `test` — verify checksums against DATs. -- `report` — generate a markdown report of matches / misses. -- `-o ${OUTPUT_DIR}/{romm}/` — output in RomM's expected platform layout (`{romm}` is Igir's RomM-layout template). -- `--only-retail` — exclude betas, hacks, unlicensed dumps. -- `--input-checksum-*` — check files thoroughly (slow but authoritative). +- `move extract`: extract archives and move the results. +- `test`: verify checksums against DATs. +- `report`: generate a markdown report of matches / misses. +- `-o ${OUTPUT_DIR}/{romm}/`: output in RomM's expected platform layout (`{romm}` is Igir's RomM-layout template). +- `--only-retail`: exclude betas, hacks, unlicensed dumps. +- `--input-checksum-*`: check files thoroughly (slow but authoritative). ## Run @@ -96,9 +96,9 @@ chmod +x igir-romm-cleanup.sh Output: -- `roms-verified/{platform-slug}/{Proper Game Name}.rom` — identified ROMs in RomM layout. -- `roms-unverified/` — whatever Igir didn't identify, still available for manual review. -- `report.csv` (or similar) — what matched, what didn't. +- `roms-verified/{platform-slug}/{Proper Game Name}.rom`: identified ROMs in RomM layout. +- `roms-unverified/`: whatever Igir didn't identify, still available for manual review. +- `report.csv` (or similar): what matched, what didn't. ## Manually migrate leftovers @@ -162,12 +162,12 @@ services: - /path/to/roms-verified:/romm/library/roms:ro ``` -Read-only is safer — if you need Igir to re-clean, you work in the parallel `roms-unverified/` and re-promote to `roms-verified/`. +Read-only is safer: if you need Igir to re-clean, you work in the parallel `roms-unverified/` and re-promote to `roms-verified/`. Run a scan from RomM. Everything should match cleanly against providers. ## See also -- [Igir docs](https://igir.io/) — the upstream reference. -- [Folder Structure](../getting-started/folder-structure.md) — what RomM expects on-disk. -- [Metadata Providers](../administration/metadata-providers.md) — how RomM matches after Igir's done its work. +- [Igir docs](https://igir.io/): the upstream reference. +- [Folder Structure](../getting-started/folder-structure.md): what RomM expects on-disk. +- [Metadata Providers](../administration/metadata-providers.md): how RomM matches after Igir's done its work. diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md index 0d390415..2bbfb6e2 100644 --- a/docs/ecosystem/index.md +++ b/docs/ecosystem/index.md @@ -5,26 +5,26 @@ description: Companion apps, feeds, and protocol references for building on top # Integrations & Ecosystem -RomM has a sizeable ecosystem of companion apps and integration patterns. This hub indexes everything — first-party and community. +RomM has a sizeable ecosystem of companion apps and integration patterns. This hub indexes everything: first-party and community. ## First-party apps Maintained by the RomM team. -- **[Argosy Launcher](argosy.md)** — Android launcher that browses and launches your RomM library on mobile. -- **[Grout](grout.md)** — Linux handheld companion for muOS / NextUI devices. -- **[Playnite Plugin](playnite-plugin.md)** — Windows desktop; imports your RomM library into [Playnite](https://playnite.link). -- **[muOS App](muos-app.md)** — official app for muOS / EmulationStation handhelds to fetch games wirelessly. +- **[Argosy Launcher](argosy.md)**: Android launcher that browses and launches your RomM library on mobile. +- **[Grout](grout.md)**: Linux handheld companion for muOS / NextUI devices. +- **[Playnite Plugin](playnite-plugin.md)**: Windows desktop; imports your RomM library into [Playnite](https://playnite.link). +- **[muOS App](muos-app.md)**: official app for muOS / EmulationStation handhelds to fetch games wirelessly. ## Feeds (for third-party apps) RomM exposes several URL feed endpoints for external homebrew / custom firmware apps that already know how to consume them. -- **[Tinfoil](tinfoil.md)** — Nintendo Switch homebrew for installing `.nsp` / `.xci` from a URL. -- **[pkgj](pkgj.md)** — PS Vita and PSP homebrew installer. -- **[fpkgi](fpkgi.md)** — PS4 / PS5 installer. -- **[Kekatsu](kekatsu.md)** — Nintendo DS multiboot loader. -- **[WebRcade](webrcade.md)** — browser-based retro console frontend. +- **[Tinfoil](tinfoil.md)**: Nintendo Switch homebrew for installing `.nsp` / `.xci` from a URL. +- **[pkgj](pkgj.md)**: PS Vita and PSP homebrew installer. +- **[fpkgi](fpkgi.md)**: PS4 / PS5 installer. +- **[Kekatsu](kekatsu.md)**: Nintendo DS multiboot loader. +- **[WebRcade](webrcade.md)**: browser-based retro console frontend. See the [full feeds reference](../reference/feeds.md) for URL formats, auth requirements, and filtering. @@ -52,17 +52,17 @@ Highlights: For developers building something new on top of RomM: -- **[Client API Tokens](client-api-tokens.md)** — how to authenticate your app, how the device-pairing flow works. -- **[Device Sync Protocol](device-sync-protocol.md)** — wire-level reference for save/state/play-session sync. -- **[API Reference](../developers/api-reference.md)** — every REST endpoint. -- **[WebSockets](../developers/websockets.md)** — live-update channels and Netplay coordination. -- **[Consuming OpenAPI](../developers/openapi.md)** — codegen patterns. +- **[Client API Tokens](client-api-tokens.md)**: how to authenticate your app, how the device-pairing flow works. +- **[Device Sync Protocol](device-sync-protocol.md)**: wire-level reference for save/state/play-session sync. +- **[API Reference](../developers/api-reference.md)**: every REST endpoint. +- **[WebSockets](../developers/websockets.md)**: live-update channels and Netplay coordination. +- **[Consuming OpenAPI](../developers/openapi.md)**: codegen patterns. ## External tooling Not a RomM companion, but useful alongside: -- **[Igir Collection Manager](igir.md)** — ROM sorting/verifying tool. Cleans up library layout before importing into RomM. +- **[Igir Collection Manager](igir.md)**: ROM sorting/verifying tool. Cleans up library layout before importing into RomM. ## Contributing a companion app diff --git a/docs/ecosystem/kekatsu.md b/docs/ecosystem/kekatsu.md index a70f56a1..e2753656 100644 --- a/docs/ecosystem/kekatsu.md +++ b/docs/ecosystem/kekatsu.md @@ -1,6 +1,6 @@ --- title: Kekatsu -description: Nintendo DS multiboot loader — install DS games from RomM via custom feed. +description: Nintendo DS multiboot loader, install DS games from RomM via custom feed. --- # Kekatsu @@ -12,7 +12,7 @@ New in RomM 5.0. ## Prerequisites - A Nintendo DS with Kekatsu installed (requires a flashcart or homebrew launcher). -- **RomM reachable from the DS over Wi-Fi** — the DS's Wi-Fi is WEP / old WPA only, so this typically means a dedicated legacy-SSID on your router or a travel router bridging the DS to your modern network. +- **RomM reachable from the DS over Wi-Fi.** The DS's Wi-Fi is WEP / old WPA only, so this typically means a dedicated legacy-SSID on your router or a travel router bridging the DS to your modern network. - DS games in `.nds` format. ## Feed URL @@ -43,19 +43,19 @@ Kekatsu can send basic auth. Either configure it on the DS side or enable `DISAB The DS's original Wi-Fi hardware supports WEP and an older WPA variant only. Modern home routers usually don't. Workarounds: -- **Dedicated DS-friendly SSID.** Many routers allow per-SSID security — add a WEP one just for the DS. +- **Dedicated DS-friendly SSID.** Many routers allow per-SSID security; add a WEP one just for the DS. - **Travel router in bridge mode.** A cheap travel router configured for WEP uplinks to your main (secure) network. -- **Use a DSi, 3DS, or homebrew replacement driver** — these support modern security. +- **Use a DSi, 3DS, or homebrew replacement driver.** These support modern security. If none of this is appealing, Kekatsu-over-LAN isn't going to work; fall back to sideloading via flashcart or similar. ## Troubleshooting -- **Feed is empty** — no `.nds` files on the `nds` platform. -- **DS can't see the network** — see the legacy-Wi-Fi section above. -- **Downloads fail** — either network timeout (LAN latency over WEP is rough) or disk space. Retry one game at a time. +- **Feed is empty.** No `.nds` files on the `nds` platform. +- **DS can't see the network.** See the legacy-Wi-Fi section above. +- **Downloads fail.** Either network timeout (LAN latency over WEP is rough) or disk space. Retry one game at a time. ## See also -- [Feeds reference](../reference/feeds.md) — all feed endpoints. -- Kekatsu upstream — find via the RomM Discord `#kekatsu` or community links (project moves). +- [Feeds reference](../reference/feeds.md): all feed endpoints. +- Kekatsu upstream: find via the RomM Discord `#kekatsu` or community links (project moves). diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md index 07f36921..891c397d 100644 --- a/docs/ecosystem/muos-app.md +++ b/docs/ecosystem/muos-app.md @@ -1,6 +1,6 @@ --- title: muOS App -description: Official RomM app for muOS and EmulationStation handhelds — fetch games wirelessly. +description: Official RomM app for muOS and EmulationStation handhelds, fetch games wirelessly. --- @@ -11,7 +11,7 @@ description: Official RomM app for muOS and EmulationStation handhelds — fetch RomM muOS logo -[muOS](https://muos.dev) is a custom firmware (CFW) for handheld devices — Anbernic, Miyoo, and similar. The **muOS App** connects to your RomM instance and fetches ROMs wirelessly. +[muOS](https://muos.dev) is a custom firmware (CFW) for handheld devices: Anbernic, Miyoo, and similar. The **muOS App** connects to your RomM instance and fetches ROMs wirelessly. - **Repo:** [rommapp/muos-app](https://github.com/rommapp/muos-app) - **Platforms:** muOS, EmulationStation (via PortMaster) @@ -19,14 +19,14 @@ description: Official RomM app for muOS and EmulationStation handhelds — fetch ## muOS-specific flavour vs Grout -This page covers the **muOS App** — a lightweight client focused on game fetching. For the fuller push/pull sync experience (saves back to RomM, play-session reporting), use [Grout](grout.md) instead. They're two different clients for the same family of devices. +This page covers the **muOS App**: a lightweight client focused on game fetching. For the fuller push/pull sync experience (saves back to RomM, play-session reporting), use [Grout](grout.md) instead. They're two different clients for the same family of devices. -- **muOS App** — lightweight; pulls ROMs, no save sync. -- **Grout** — full sync; ROMs + saves + states + play sessions. +- **muOS App**: lightweight; pulls ROMs, no save sync. +- **Grout**: full sync; ROMs + saves + states + play sessions. Pick based on what you need. -## Installing — muOS +## Installing: muOS Installation uses muOS's [Archive Manager](https://muos.dev/installation/archive): @@ -49,7 +49,7 @@ Installation uses muOS's [Archive Manager](https://muos.dev/installation/archive Once RomM's 5.0 Client API Token flow is wired into the muOS app (planned), prefer pairing via token instead of password. -## Installing — EmulationStation (via PortMaster) +## Installing: EmulationStation (via PortMaster) For EmulationStation-based devices: @@ -70,7 +70,7 @@ Simplest setup: More-involved setups: - **Reverse proxy with TLS.** `HOST=https://romm.example.com`. HTTPS works but introduces cert-validation risk on handhelds (some fail strict TLS). -- **Remote access via VPN.** Install Tailscale or similar on the handheld (if supported) — lets the handheld reach RomM from outside the LAN. +- **Remote access via VPN.** Install Tailscale or similar on the handheld (if supported); lets the handheld reach RomM from outside the LAN. ## Using the app @@ -80,21 +80,21 @@ Downloaded games appear in the device's usual ROM folder for the platform, so mu ## What it doesn't do (yet) -- **Save sync** — this app is pull-only. For bidirectional sync, use [Grout](grout.md). -- **Play session tracking** — not ingested into RomM. -- **Firmware download** — not in scope. +- **Save sync.** This app is pull-only. For bidirectional sync, use [Grout](grout.md). +- **Play session tracking.** Not ingested into RomM. +- **Firmware download.** Not in scope. If you need these, Grout is the app. ## Troubleshooting -- **Can't connect** — wrong `HOST` in `.env`, or the handheld isn't on the same network as RomM. Ping RomM's IP from the handheld's shell to confirm reachability. -- **"Authentication failed"** — password wrong, or `DISABLE_USERPASS_LOGIN=true` on the RomM side. Either re-enable user/pass login or use a token once supported. -- **Downloaded games don't show in the platform** — refresh the library from muOS's UI. If they still don't appear, the platform folder in `HOST_PATH` is wrong — check muOS's expected layout. +- **Can't connect.** Wrong `HOST` in `.env`, or the handheld isn't on the same network as RomM. Ping RomM's IP from the handheld's shell to confirm reachability. +- **"Authentication failed".** Password wrong, or `DISABLE_USERPASS_LOGIN=true` on the RomM side. Either re-enable user/pass login or use a token once supported. +- **Downloaded games don't show in the platform.** Refresh the library from muOS's UI. If they still don't appear, the platform folder in `HOST_PATH` is wrong; check muOS's expected layout. ## See also -- [Grout](grout.md) — the fuller sync client for the same device family. -- [Client API Tokens](client-api-tokens.md) — safer auth than plaintext credentials in `.env`. -- [Mobile & TV](../using/mobile-and-tv.md) — handheld usage patterns. -- [rommapp/muos-app](https://github.com/rommapp/muos-app) — source, issues, releases. +- [Grout](grout.md): the fuller sync client for the same device family. +- [Client API Tokens](client-api-tokens.md): safer auth than plaintext credentials in `.env`. +- [Mobile & TV](../using/mobile-and-tv.md): handheld usage patterns. +- [rommapp/muos-app](https://github.com/rommapp/muos-app): source, issues, releases. diff --git a/docs/ecosystem/pkgj.md b/docs/ecosystem/pkgj.md index 4b7ffa3e..647c7641 100644 --- a/docs/ecosystem/pkgj.md +++ b/docs/ecosystem/pkgj.md @@ -10,9 +10,9 @@ description: Install PS Vita and PSP games from your RomM library via pkgj homeb ## Prerequisites - **PS Vita** with [pkgj](https://github.com/blastrock/pkgj) installed. -- A way to edit files on the Vita — [VitaShell](https://github.com/TheOfficialFloW/VitaShell) works well. -- **RomM reachable from the Vita** — same LAN ideal; HTTP or HTTPS both work. -- Your games stored as `.pkg` files (pkgj requires this format — it won't work with `.iso` or other formats). +- A way to edit files on the Vita: [VitaShell](https://github.com/TheOfficialFloW/VitaShell) works well. +- **RomM reachable from the Vita**: same LAN ideal; HTTP or HTTPS both work. +- Your games stored as `.pkg` files (pkgj requires this format; it won't work with `.iso` or other formats). ## Feed URLs @@ -59,18 +59,18 @@ If you have non-`.pkg` files you want on the Vita, you'll need to convert them o The pkgi feeds honour basic auth. If your RomM doesn't have `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`, pkgj sends basic auth headers. -Unlike [Tinfoil](tinfoil.md), pkgj handles auth natively — you don't have to turn off download auth on RomM to use it. Still, some users prefer disabling auth for a smoother first-time flow; either path works. +Unlike [Tinfoil](tinfoil.md), pkgj handles auth natively: you don't have to turn off download auth on RomM to use it. Still, some users prefer disabling auth for a smoother first-time flow; either path works. ## Troubleshooting -- **"can't get list: list is empty"** — you don't have any `.pkg` content for the feeds you configured. Check your library actually contains `.pkg` files on the corresponding platforms. -- **Refresh returns an error** — URL in `config.txt` is wrong. Verify the feed URL in a browser first (returns JSON). -- **Download fails partway** — LAN connectivity or disk space on Vita. pkgj reports both. -- **Game installs but won't boot** — the `.pkg` is for a different firmware / region. Not a RomM issue. +- **"can't get list: list is empty".** You don't have any `.pkg` content for the feeds you configured. Check your library actually contains `.pkg` files on the corresponding platforms. +- **Refresh returns an error.** URL in `config.txt` is wrong. Verify the feed URL in a browser first (returns JSON). +- **Download fails partway.** LAN connectivity or disk space on Vita. pkgj reports both. +- **Game installs but won't boot.** The `.pkg` is for a different firmware / region. Not a RomM issue. ## See also -- [Feeds reference](../reference/feeds.md) — full feeds catalogue. -- [Tinfoil](tinfoil.md) — Switch equivalent. -- [fpkgi](fpkgi.md) — PS4 / PS5 equivalent. -- [pkgj](https://github.com/blastrock/pkgj) — upstream homebrew. +- [Feeds reference](../reference/feeds.md): full feeds catalogue. +- [Tinfoil](tinfoil.md): Switch equivalent. +- [fpkgi](fpkgi.md): PS4 / PS5 equivalent. +- [pkgj](https://github.com/blastrock/pkgj): upstream homebrew. diff --git a/docs/ecosystem/playnite-plugin.md b/docs/ecosystem/playnite-plugin.md index 17278b42..d9f130fa 100644 --- a/docs/ecosystem/playnite-plugin.md +++ b/docs/ecosystem/playnite-plugin.md @@ -11,7 +11,7 @@ description: Import your RomM library into Playnite on Windows. romm[playnite] logo -[Playnite](https://playnite.link) is a unified, open-source game library manager for Windows — one place to launch Steam, Epic, GOG, RetroArch, and emulated games. This plugin imports your RomM library into Playnite, so your RomM ROMs appear alongside your Steam games. +[Playnite](https://playnite.link) is a unified, open-source game library manager for Windows: one place to launch Steam, Epic, GOG, RetroArch, and emulated games. This plugin imports your RomM library into Playnite, so your RomM ROMs appear alongside your Steam games. - **Repo:** [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin) - **Language:** C# @@ -27,7 +27,7 @@ description: Import your RomM library into Playnite on Windows. ## Installing -Four paths — pick whichever's easiest: +Four paths, pick whichever's easiest: - **A.** Paste this URL into a browser to launch Playnite and install automatically: `playnite://playnite/installaddon/RomM_9700aa21-447d-41b4-a989-acd38f407d9f` @@ -49,11 +49,11 @@ Playnite: **Menu → Library → Configure Integrations → RomM**. #### Authentication -- **Host URL** — your RomM URL, including scheme, no trailing slash. Examples: +- **Host URL**: your RomM URL, including scheme, no trailing slash. Examples: - ✓ `https://romm.example.com` - ✗ `romm.example.com` - ✗ `https://romm.example.com/` -- **Username + password** — Playnite stores these in plaintext. Use a **dedicated Viewer-role account** for Playnite, not your admin account. +- **Username + password**: Playnite stores these in plaintext. Use a **dedicated Viewer-role account** for Playnite, not your admin account. !!! tip "Use a Client API Token instead" If your RomM version supports API tokens, create a Viewer-scoped [Client API Token](client-api-tokens.md) and use it instead of a password. Safer and easier to revoke. @@ -68,8 +68,8 @@ One mapping per platform: | Emulator Profile | The profile within the emulator | Nintendo GameCube | ✓ | | Platform | The RomM platform slug | Nintendo GameCube | ✓ | | Destination Path | Where downloaded ROMs are stored | `C:\roms\gc` | ✓ | -| Auto-extract | Unpack zipped ROMs after download | | — | -| Enabled | Whether this mapping is active | | — | +| Auto-extract | Unpack zipped ROMs after download | | | +| Enabled | Whether this mapping is active | | | Map every platform you want Playnite to see. Platforms without a mapping are skipped during import. @@ -102,14 +102,14 @@ For automatic refresh: use Playnite's scheduled-library-refresh add-on, or manua ## Troubleshooting -- **"Unable to connect"** — check the URL scheme, host, and that RomM is reachable from this machine (corporate firewalls, etc.). -- **"Authentication failed"** — username/password mismatch, or the account is disabled. If using a token, make sure it has `roms.read` + `platforms.read`. -- **No games imported** — emulator path mappings don't cover any platforms in your RomM library. Add mappings for your platforms. -- **Install fails** — destination path isn't writable, or disk is full. -- **Wrong emulator launches** — fix the mapping for that platform. +- **"Unable to connect".** Check the URL scheme, host, and that RomM is reachable from this machine (corporate firewalls, etc.). +- **"Authentication failed".** Username/password mismatch, or the account is disabled. If using a token, make sure it has `roms.read` + `platforms.read`. +- **No games imported.** Emulator path mappings don't cover any platforms in your RomM library. Add mappings for your platforms. +- **Install fails.** Destination path isn't writable, or disk is full. +- **Wrong emulator launches.** Fix the mapping for that platform. ## See also -- [Client API Tokens](client-api-tokens.md) — recommended auth method. -- [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin) — source, issues, releases. -- [Playnite docs](https://playnite.link/docs/) — Playnite basics if you're new to it. +- [Client API Tokens](client-api-tokens.md): recommended auth method. +- [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin): source, issues, releases. +- [Playnite docs](https://playnite.link/docs/): Playnite basics if you're new to it. diff --git a/docs/ecosystem/tinfoil.md b/docs/ecosystem/tinfoil.md index 95efcccc..bcdf446e 100644 --- a/docs/ecosystem/tinfoil.md +++ b/docs/ecosystem/tinfoil.md @@ -16,7 +16,7 @@ description: Install Nintendo Switch games from your RomM library over Wi-Fi via ## Prerequisites - **RomM 3.5.0 or newer.** Tinfoil feeds landed in that release. Much better in 5.0. -- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`** on your RomM instance. Tinfoil can't send a bearer token, so the downloads endpoint has to be openable. **Only enable this when RomM isn't directly exposed to the public internet** — see [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass). +- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`** on your RomM instance. Tinfoil can't send a bearer token, so the downloads endpoint has to be openable. **Only enable this when RomM isn't directly exposed to the public internet.** See [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass). - **Tinfoil installed on the Switch.** Setup varies; follow Tinfoil's own docs. - **A Switch that can reach RomM over Wi-Fi.** Same LAN is easiest; remote reachability requires reverse proxy + cert that the Switch accepts. @@ -26,7 +26,7 @@ description: Install Nintendo Switch games from your RomM library over Wi-Fi via {romm_url}/api/feeds/tinfoil ``` -No authentication — the endpoint works as long as `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`. +No authentication: the endpoint works as long as `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`. ## Configuring Tinfoil @@ -37,7 +37,7 @@ No authentication — the endpoint works as long as `DISABLE_DOWNLOAD_ENDPOINT_A - **Host:** RomM's hostname or IP. - **Port:** RomM's port (usually 80 or 443). - **Path:** `/api/feeds/tinfoil` - - **Username:** your RomM username (optional — Tinfoil can send basic auth; RomM tries it). + - **Username:** your RomM username (optional; Tinfoil can send basic auth; RomM tries it). - **Password:** your RomM password. - **Title:** anything (e.g. `RomM Switch`). - **Enabled:** yes. @@ -50,12 +50,12 @@ On reopen, you should see a custom message of the day: `RomM Switch Library`. If ## Using it -- **New Games** tab in Tinfoil — browseable list of your Switch ROMs. -- **File Browser** — pick a file to install directly. +- **New Games** tab in Tinfoil: browseable list of your Switch ROMs. +- **File Browser**: pick a file to install directly. Tinfoil handles the install flow like any homebrew: downloads the `.nsp` / `.xci`, installs to eMMC or SD, cleans up. -## Filename requirements — TitleIDs +## Filename requirements: TitleIDs Tinfoil needs **Switch title IDs** in the filenames to parse and categorise games. The format: @@ -68,7 +68,7 @@ The bracketed `[0100000000010000]` is the title ID. Without it, Tinfoil shows th ![TitleID example](../resources/tinfoil/titleid.jpg) !!! info "Improvement coming" - 5.0 improves RomM's title-ID handling — it auto-detects title IDs from filenames that have them and feeds Tinfoil regardless of whether your filename format is standard. The guidance above still applies for older RomM releases. + 5.0 improves RomM's title-ID handling: it auto-detects title IDs from filenames that have them and feeds Tinfoil regardless of whether your filename format is standard. The guidance above still applies for older RomM releases. ### Finding title IDs @@ -93,12 +93,12 @@ This gets you authenticated Tinfoil feeds without making RomM itself world-reada ## Troubleshooting -- **"can't get list: list is empty"** — either your RomM library has no `.nsp`/`.xci` files that Tinfoil recognises, or filenames lack title IDs. -- **Tinfoil connects but nothing in New Games** — title IDs missing from filenames. Rename. -- **Tinfoil can't connect at all** — LAN reachability issue, or wrong port in the feed setup. Try `http://:/api/feeds/tinfoil` in a browser; you should get JSON. -- **Downloads fail with 401** — `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` isn't set on RomM, or you forgot to restart the container after setting it. +- **"can't get list: list is empty".** Either your RomM library has no `.nsp`/`.xci` files that Tinfoil recognises, or filenames lack title IDs. +- **Tinfoil connects but nothing in New Games.** Title IDs missing from filenames. Rename. +- **Tinfoil can't connect at all.** LAN reachability issue, or wrong port in the feed setup. Try `http://:/api/feeds/tinfoil` in a browser; you should get JSON. +- **Downloads fail with 401.** `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` isn't set on RomM, or you forgot to restart the container after setting it. ## See also -- [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass) — the `DISABLE_DOWNLOAD_ENDPOINT_AUTH` caveat. -- [Feeds reference](../reference/feeds.md) — full feeds catalogue. +- [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass): the `DISABLE_DOWNLOAD_ENDPOINT_AUTH` caveat. +- [Feeds reference](../reference/feeds.md): full feeds catalogue. diff --git a/docs/ecosystem/webrcade.md b/docs/ecosystem/webrcade.md index e1f77ad6..43961f78 100644 --- a/docs/ecosystem/webrcade.md +++ b/docs/ecosystem/webrcade.md @@ -1,11 +1,11 @@ --- title: WebRcade -description: Load your RomM library into WebRcade — an alternative browser-based retro frontend. +description: Load your RomM library into WebRcade, an alternative browser-based retro frontend. --- # WebRcade -[WebRcade](https://www.webrcade.com/) is a browser-based retro console frontend. It plays games in-browser, similar to RomM's built-in EmulatorJS player — the difference is UX: WebRcade has its own aesthetic, a curated preset-app model, and feed-based import. +[WebRcade](https://www.webrcade.com/) is a browser-based retro console frontend. It plays games in-browser, similar to RomM's built-in EmulatorJS player. The difference is UX: WebRcade has its own aesthetic, a curated preset-app model, and feed-based import. If you prefer WebRcade's look-and-feel but want to point it at your RomM library, use the feed endpoint below. @@ -20,7 +20,7 @@ WebRcade-compatible JSON listing every ROM in your library with metadata and dir ## Setting up WebRcade 1. Open [WebRcade.com](https://www.webrcade.com/) in a browser. -2. Create an account (or use it unauthenticated — limited features). +2. Create an account (or use it unauthenticated, with limited features). 3. Go to **My WebRcade → Add Feed**. 4. Enter RomM's feed URL: `{romm_url}/api/feeds/webrcade`. 5. Save. Your RomM library appears as WebRcade apps. @@ -32,7 +32,7 @@ The `/api/feeds/webrcade` endpoint sends basic auth if WebRcade provides credent - Configure basic auth on WebRcade's feed-add screen (if it offers that), OR - Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` on RomM. -The same security caveats apply — see [Tinfoil prerequisites](tinfoil.md#prerequisites) for context on turning off download auth. +The same security caveats apply; see [Tinfoil prerequisites](tinfoil.md#prerequisites) for context on turning off download auth. ## RomM vs WebRcade @@ -46,18 +46,18 @@ When to stay with RomM's player: - **You want library management.** WebRcade is frontend-only; RomM owns the metadata and scanning. - **You want user accounts + collections + per-user progress.** WebRcade is single-user-ish. -- **You want [Netplay](../using/netplay.md)** — RomM has it; WebRcade doesn't. +- **You want [Netplay](../using/netplay.md).** RomM has it; WebRcade doesn't. -Totally reasonable to run both — WebRcade as a launcher UI pointed at RomM for the library. +Totally reasonable to run both: WebRcade as a launcher UI pointed at RomM for the library. ## Troubleshooting -- **Feed shows no games** — WebRcade filters by its own supported-platform set. Games on unsupported platforms won't show. -- **Games don't launch** — the ROM format isn't supported by WebRcade's emulator cores (which differ from EmulatorJS's). Check WebRcade's supported list. -- **Download fails** — same auth / URL issues as other feeds. Check `{romm_url}/api/feeds/webrcade` returns JSON in a browser. +- **Feed shows no games.** WebRcade filters by its own supported-platform set. Games on unsupported platforms won't show. +- **Games don't launch.** The ROM format isn't supported by WebRcade's emulator cores (which differ from EmulatorJS's). Check WebRcade's supported list. +- **Download fails.** Same auth / URL issues as other feeds. Check `{romm_url}/api/feeds/webrcade` returns JSON in a browser. ## See also -- [Feeds reference](../reference/feeds.md) — all feed endpoints. -- [In-Browser Play](../using/in-browser-play.md) — RomM's built-in player. -- [WebRcade](https://www.webrcade.com/) — upstream frontend. +- [Feeds reference](../reference/feeds.md): all feed endpoints. +- [In-Browser Play](../using/in-browser-play.md): RomM's built-in player. +- [WebRcade](https://www.webrcade.com/): upstream frontend. diff --git a/docs/getting-started/concepts.md b/docs/getting-started/concepts.md index 4b371135..387724d6 100644 --- a/docs/getting-started/concepts.md +++ b/docs/getting-started/concepts.md @@ -13,7 +13,7 @@ Your ROM files on disk. RomM reads the library (usually read-only) and builds it ## Platform -A gaming system — SNES, PlayStation, Game Boy Advance, DOS, etc. RomM ships with ~400 supported platforms (see [Supported Platforms](../platforms/supported-platforms.md)). Each platform has a **slug** (`snes`, `ps`, `gba`) that doubles as the folder name it looks for in your library. You can override the mapping from folder name to slug via `config.yml`. +A gaming system: SNES, PlayStation, Game Boy Advance, DOS, etc. RomM ships with ~400 supported platforms (see [Supported Platforms](../platforms/supported-platforms.md)). Each platform has a **slug** (`snes`, `ps`, `gba`) that doubles as the folder name it looks for in your library. You can override the mapping from folder name to slug via `config.yml`. ## ROM @@ -21,7 +21,7 @@ A single game entry. One filesystem file, one folder of files (multi-disc, patch ## Metadata Provider -An external source of game data — IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, PlayMatch, LaunchBox, SteamGridDB, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro. RomM queries one or more of them during a scan and merges the results. Configured via env vars + priority in `config.yml`. See [Metadata Providers](../administration/metadata-providers.md). +An external source of game data: IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, PlayMatch, LaunchBox, SteamGridDB, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro. RomM queries one or more of them during a scan and merges the results. Configured via env vars + priority in `config.yml`. See [Metadata Providers](../administration/metadata-providers.md). ## Scan @@ -31,9 +31,9 @@ The process of walking the library, hashing files, calling metadata providers, a A named grouping of ROMs. Three flavours: -- **Standard** — a hand-curated list. You pick what's in it. -- **Smart** — rule-based and auto-populating. Define a query ("all SNES games rated 4+ stars"); RomM keeps it in sync. -- **Virtual** — auto-generated by RomM itself (by genre, developer, year, tag). Not user-editable; toggle on/off in UI settings. +- **Standard**: a hand-curated list. You pick what's in it. +- **Smart**: rule-based and auto-populating. Define a query ("all SNES games rated 4+ stars"); RomM keeps it in sync. +- **Virtual**: auto-generated by RomM itself (by genre, developer, year, tag). Not user-editable; toggle on/off in UI settings. ## Asset @@ -50,7 +50,7 @@ An account with a role (Viewer, Editor, Admin) and a set of scopes. Can be creat ## Role & Scope - **Roles** (Viewer / Editor / Admin) are convenient bundles of scopes. -- **Scopes** are fine-grained permissions (`roms.read`, `collections.write`, `users.write`, `tasks.run` — 19 total). +- **Scopes** are fine-grained permissions (`roms.read`, `collections.write`, `users.write`, `tasks.run`; 19 total). Tokens and OIDC sessions carry subsets of scopes. Every endpoint requires specific scopes. See the [scope matrix](../administration/users-and-roles.md#scope-matrix). @@ -60,24 +60,24 @@ A long-lived bearer token scoped to a user. Used by companion apps (Argosy, Grou ## Device -A registered endpoint that syncs with RomM — a handheld running Grout, an Android phone running Argosy, a SteamDeck running DeckRommSync. Devices pull saves and states; some push them back after a session. See [Device Sync Protocol](../ecosystem/device-sync-protocol.md). +A registered endpoint that syncs with RomM: a handheld running Grout, an Android phone running Argosy, a SteamDeck running DeckRommSync. Devices pull saves and states; some push them back after a session. See [Device Sync Protocol](../ecosystem/device-sync-protocol.md). ## Play Session -A timestamped record of someone playing a ROM — start, end, duration, device. Used by RomM's stats, the Continue Playing ribbon, and per-ROM playtime totals. Ingested automatically when playing in-browser; companion apps push them via API. +A timestamped record of someone playing a ROM: start, end, duration, device. Used by RomM's stats, the Continue Playing ribbon, and per-ROM playtime totals. Ingested automatically when playing in-browser; companion apps push them via API. ## Task -A unit of background work — scan, metadata sync, cleanup, device sync. Runs through RQ (Redis Queue). Can be scheduled (cron), watcher-triggered, or manual. See [Scheduled Tasks](../administration/scheduled-tasks.md). +A unit of background work: scan, metadata sync, cleanup, device sync. Runs through RQ (Redis Queue). Can be scheduled (cron), watcher-triggered, or manual. See [Scheduled Tasks](../administration/scheduled-tasks.md). ## Resources & Assets directories (confusing, named similarly, not the same thing) -- **`/romm/resources`** — **machine-managed**. Cover art, screenshots, manuals fetched from metadata providers. Can be rebuilt from a rescan. -- **`/romm/assets`** — **user-owned**. Saves, states, user-uploaded screenshots. Back this up; it's not recoverable. +- **`/romm/resources`**: **machine-managed**. Cover art, screenshots, manuals fetched from metadata providers. Can be rebuilt from a rescan. +- **`/romm/assets`**: **user-owned**. Saves, states, user-uploaded screenshots. Back this up; it's not recoverable. ## Console Mode -A separate `/console` UI optimised for gamepads and TV displays — spatial navigation, bigger hit targets, SFX, no mouse required. Accessible from the same RomM instance, same data. See [Console Mode](../using/console-mode.md). +A separate `/console` UI optimised for gamepads and TV displays: spatial navigation, bigger hit targets, SFX, no mouse required. Accessible from the same RomM instance, same data. See [Console Mode](../using/console-mode.md). ## Kiosk Mode @@ -89,7 +89,7 @@ The two in-browser emulators. **EmulatorJS** handles retro consoles (NES, SNES, ## Netplay -EmulatorJS's multiplayer mode — two or more players share a session across the internet. RomM tracks open rooms and brokers them via WebSocket. Needs STUN/TURN (ICE servers) configured in `config.yml` for reliable NAT traversal. See [Netplay](../using/netplay.md). +EmulatorJS's multiplayer mode: two or more players share a session across the internet. RomM tracks open rooms and brokers them via WebSocket. Needs STUN/TURN (ICE servers) configured in `config.yml` for reliable NAT traversal. See [Netplay](../using/netplay.md). --- diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md index 065f5d08..276f43c4 100644 --- a/docs/getting-started/first-scan.md +++ b/docs/getting-started/first-scan.md @@ -11,9 +11,9 @@ You've got RomM running ([Quick Start](quick-start.md)), your ROMs are laid out A fifteen-second check that saves hours: -- **Library is mounted** — on the host, `ls /path/to/library` should show your `roms/` (or per-platform) folders. If it doesn't, the mount is wrong. -- **At least one metadata provider is configured** — RomM will scan without one, but every game comes back "unmatched" and you'll have nothing useful to look at. -- **The Setup Wizard is done** — if RomM is still showing the wizard, finish that first. The first user becomes Admin. +- **Library is mounted**: on the host, `ls /path/to/library` should show your `roms/` (or per-platform) folders. If it doesn't, the mount is wrong. +- **At least one metadata provider is configured**: RomM will scan without one, but every game comes back "unmatched" and you'll have nothing useful to look at. +- **The Setup Wizard is done**: if RomM is still showing the wizard, finish that first. The first user becomes Admin. ## Run the scan @@ -25,9 +25,9 @@ A fifteen-second check that saves hours: The page switches to live mode: -- A **log** on the right streams everything the scanner is doing — file hashed, provider queried, match found or not. +- A **log** on the right streams everything the scanner is doing: file hashed, provider queried, match found or not. - Per-platform **accordion panels** show counts update live: total found, matched, unmatched. -- You can click a **matched ROM** while the scan is still running to see what metadata RomM pulled — no need to wait for the full run. +- You can click a **matched ROM** while the scan is still running to see what metadata RomM pulled, with no need to wait for the full run. First scans on big libraries take a while. Expect ~1 second per ROM with a fast network to IGDB/ScreenScraper; hashing (which runs unless you disabled it) adds IO time proportional to file size. @@ -38,16 +38,16 @@ For each ROM the scanner: 1. **Computes hashes** (CRC32, MD5, SHA1, plus RetroAchievements-specific hashes). 2. **Hits the configured metadata providers** in priority order. First provider to recognise the file (by hash or filename) becomes the initial match. 3. **Writes the DB entry** with title, cover, description, release date, and anything else the winning provider returned. -4. **Merges overlay data** from other providers — RetroAchievements progression, HowLongToBeat completion times, SteamGridDB alternate covers if you've asked. +4. **Merges overlay data** from other providers: RetroAchievements progression, HowLongToBeat completion times, SteamGridDB alternate covers if you've asked. An **unmatched** ROM means no provider recognised it. Common causes: - Filename is too generic (`game.gba`). - Bad rip, intro / patch applied, or a regional variant no provider has indexed. -- Platform folder misnamed — the scanner queries providers scoped to the detected platform; wrong platform = no results. -- Metadata provider credentials wrong or rate-limited — check the scan log for errors. +- Platform folder misnamed: the scanner queries providers scoped to the detected platform; wrong platform = no results. +- Metadata provider credentials wrong or rate-limited: check the scan log for errors. -Most of these are fixable — see [Scanning Troubleshooting](../troubleshooting/scanning.md). +Most of these are fixable; see [Scanning Troubleshooting](../troubleshooting/scanning.md). ## When the scan finishes @@ -55,22 +55,22 @@ Click the **RomM logo** (top-left) to go home. You should see: - Platform cards for each folder it scanned. - A **Recently Added** carousel on the dashboard. -- A **Continue Playing** section — empty until you play something. +- A **Continue Playing** section: empty until you play something. From here, typical next steps: -- **Browse** — click a platform card, flip through the grid. -- **Fix unmatched ROMs** — rename or re-tag; re-run an **Unmatched** scan to pick them up. See [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). -- **Tweak priorities** — if ScreenScraper's covers are nicer than IGDB's for your library, reorder `scan.priority.artwork` in [`config.yml`](../reference/configuration-file.md). -- **Add more users** — [Invitations & Registration](../administration/invitations-and-registration.md). +- **Browse**: click a platform card, flip through the grid. +- **Fix unmatched ROMs**: rename or re-tag; re-run an **Unmatched** scan to pick them up. See [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). +- **Tweak priorities**: if ScreenScraper's covers are nicer than IGDB's for your library, reorder `scan.priority.artwork` in [`config.yml`](../reference/configuration-file.md). +- **Add more users**: [Invitations & Registration](../administration/invitations-and-registration.md). ## Skip to a targeted scan If you're adding ROMs later and don't want a full rescan: -- **New Platforms** — only scans folders RomM hasn't seen before. Fast. -- **Quick** — skips ROMs already catalogued. Good default for "I added a few games". -- **Unmatched** — re-runs matching against ROMs without a provider ID. Good after adding a metadata provider. +- **New Platforms**: only scans folders RomM hasn't seen before. Fast. +- **Quick**: skips ROMs already catalogued. Good default for "I added a few games". +- **Unmatched**: re-runs matching against ROMs without a provider ID. Good after adding a metadata provider. All six scan modes are documented in [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). @@ -81,6 +81,6 @@ Don't want to keep clicking Scan? - **Scheduled scans** run nightly by default. Tune with `SCAN_INTERVAL_CRON`. - **The filesystem watcher** can auto-scan when files appear or disappear. Enable with `WATCHER_ENABLED=true`. Details and tradeoffs in [Scanning & Watcher](../administration/scanning-and-watcher.md). -## It's working — what next? +## It's working: what next? Back to the [Introduction](../index.md) to pick what you want to do next: set up users, fine-tune admin settings, or just dive in and play. If something's wrong, start with [Scanning Troubleshooting](../troubleshooting/scanning.md). diff --git a/docs/getting-started/folder-structure.md b/docs/getting-started/folder-structure.md index ea5a9ea2..98415e7b 100644 --- a/docs/getting-started/folder-structure.md +++ b/docs/getting-started/folder-structure.md @@ -7,27 +7,27 @@ description: How to organise your ROM library on disk so RomM can scan and match # Folder Structure -RomM expects your library to be organised in one of two layouts. It tries **Structure A** first, and falls back to **Structure B** if A isn't found. This auto-detection is per-library, so you don't pick one up front — just arrange files the way you prefer, and RomM figures it out. +RomM expects your library to be organised in one of two layouts. It tries **Structure A** first, and falls back to **Structure B** if A isn't found. This auto-detection is per-library, so you don't pick one up front; just arrange files the way you prefer, and RomM figures it out. ## The two layouts Both layouts separate ROMs from BIOS files. They differ on whether the split lives at the top of the tree or inside each platform. -- **Structure A (recommended)** — one top-level `roms/`, one top-level `bios/`, platforms nested inside each. +- **Structure A (recommended)**: one top-level `roms/`, one top-level `bios/`, platforms nested inside each. ```text /roms/{platform}/ /bios/{platform}/ ``` -- **Structure B (fallback)** — one folder per platform at the top, `roms/` and `bios/` inside each. +- **Structure B (fallback)**: one folder per platform at the top, `roms/` and `bios/` inside each. ```text /{platform}/roms/ /{platform}/bios/ ``` -The BIOS / firmware tree is **optional** — only platforms that require firmware for emulation need it. +The BIOS / firmware tree is **optional**: only platforms that require firmware for emulation need it. ### Mount point @@ -40,7 +40,7 @@ See the [reference Docker Compose](../install/docker-compose.md) for where `/rom ## Multi-file games -Some games come as **folders** instead of single files — multi-disc, DLC, manuals, patches. RomM understands this layout and recognises these sub-folder names, surfacing them as tags in the UI: +Some games come as **folders** instead of single files: multi-disc, DLC, manuals, patches. RomM understands this layout and recognises these sub-folder names, surfacing them as tags in the UI: - `dlc` - `hack` @@ -164,17 +164,17 @@ Some games come as **folders** instead of single files — multi-disc, DLC, manu ## Customising behaviour -The on-disk layout is only half the story. Per-library exclusions, custom platform bindings, and metadata source priority all live in [`config.yml`](../reference/configuration-file.md). You can edit the file directly, or go through **Administration → Library Management** in the web UI — they're two views of the same data. +The on-disk layout is only half the story. Per-library exclusions, custom platform bindings, and metadata source priority all live in [`config.yml`](../reference/configuration-file.md). You can edit the file directly, or go through **Administration → Library Management** in the web UI; they're two views of the same data. ## Naming convention Filenames are parsed for region, language, revision, and arbitrary tags. Both `[]` and `()` delimiters work. -- **Region / language** — both ISO-like codes and full names. Add a custom region or language by prefixing with `reg` / `reg-` (e.g. `reg MyOwnLang` or `reg-MyOwnLang`). -- **Revision** — prefix with `rev` / `rev-`. Example: `rev v1`, `rev-1`. -- **Arbitrary tags** — anything else in brackets is imported verbatim. Example: `tetris [1.0001](HACK)[!].gba`. +- **Region / language**: both ISO-like codes and full names. Add a custom region or language by prefixing with `reg` / `reg-` (e.g. `reg MyOwnLang` or `reg-MyOwnLang`). +- **Revision**: prefix with `rev` / `rev-`. Example: `rev v1`, `rev-1`. +- **Arbitrary tags**: anything else in brackets is imported verbatim. Example: `tetris [1.0001](HACK)[!].gba`. -Tags are searchable in the search bar — typing `(USA)` returns every game tagged USA. +Tags are searchable in the search bar: typing `(USA)` returns every game tagged USA.
@@ -247,4 +247,4 @@ Tags are searchable in the search bar — typing `(USA)` returns every game tagg ## Filename metadata tags -RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry — covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). +RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry, covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index 9e5c1ac0..5dbb7aff 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -20,7 +20,7 @@ You'll need: ## 1. Write your `docker-compose.yml` -Start from the reference file shipped in the RomM repo — a known-good, minimally-edited version is included below. Save it as `docker-compose.yml` in an empty directory on your host. +Start from the reference file shipped in the RomM repo; a known-good, minimally-edited version is included below. Save it as `docker-compose.yml` in an empty directory on your host. ???+ example "docker-compose.yml" ``` yaml @@ -31,7 +31,7 @@ You'll want to edit the following values before launching: | Where | Variable | What to put | | --- | --- | --- | -| `romm-db` service | `MARIADB_ROOT_PASSWORD` | A long random password. Generate one — don't reuse. | +| `romm-db` service | `MARIADB_ROOT_PASSWORD` | A long random password. Generate one; don't reuse. | | `romm-db` service | `MARIADB_PASSWORD` | A separate long random password for the `romm-user`. | | `romm` service | `DB_PASSWD` | Must match `MARIADB_PASSWORD` above. | | `romm` service | `ROMM_AUTH_SECRET_KEY` | Generate with `openssl rand -hex 32`. Keep it secret. | @@ -79,7 +79,7 @@ docker ps -f name=romm ## 3. Create the admin user -Open `http://:80` in a browser. The first time RomM starts, you'll be redirected to the **Setup Wizard**. Set an admin username and password — the first user created always gets the Admin role — then log in. +Open `http://:80` in a browser. The first time RomM starts, you'll be redirected to the **Setup Wizard**. Set an admin username and password (the first user created always gets the Admin role), then log in. ## 4. Scan your library @@ -90,7 +90,7 @@ The fastest way to populate RomM is to let it scan your mounted library: 3. Start the scan. You can open any matched game while the scan continues to see the metadata RomM pulled. 4. When the scan finishes, click the RomM logo to return home. You'll see your platforms and the most recently added games. -That's it — you're running RomM. From here: +That's it, you're running RomM. From here: - Customise the on-disk layout: [Folder Structure](folder-structure.md) - Add more users and lock down access: [Users & Roles](../administration/users-and-roles.md) @@ -99,7 +99,7 @@ That's it — you're running RomM. From here: ## Alternative: upload ROMs through the UI -If your library isn't mounted (yet) or you're just adding a handful of files, the upload dialog is fine for small batches — not recommended for the initial import of a large collection, and it doesn't handle multi-file ROMs: +If your library isn't mounted (yet) or you're just adding a handful of files, the upload dialog is fine for small batches. Not recommended for the initial import of a large collection, and it doesn't handle multi-file ROMs: 1. Click **Upload** in the sidebar. 2. Choose the target platform, click **+ ADD**, and select the files to upload. diff --git a/docs/getting-started/what-is-new-in-5.md b/docs/getting-started/what-is-new-in-5.md index 2f737c85..a4b66cba 100644 --- a/docs/getting-started/what-is-new-in-5.md +++ b/docs/getting-started/what-is-new-in-5.md @@ -1,6 +1,6 @@ --- title: What's New in 5.0 -description: The highlights of the RomM 5.0 release — features, changes, and what it means for you. +description: The highlights of the RomM 5.0 release: features, changes, and what it means for you. --- # What's New in 5.0 @@ -8,20 +8,20 @@ description: The highlights of the RomM 5.0 release — features, changes, and w RomM 5.0 is the biggest release since the project started. Here's what's landed, organised by who it's for. !!! warning "Upgrading from 4.x?" - Read the [**Upgrading to 5.0**](../releases/upgrading-to-5.0.md) guide before you pull the new image. There are breaking changes — migrations, env var renames, and config shifts. + Read the [**Upgrading to 5.0**](../releases/upgrading-to-5.0.md) guide before you pull the new image. There are breaking changes: migrations, env var renames, and config shifts. ## For end users ### Console Mode -A brand-new `/console` UI designed for TVs and gamepads. Spatial navigation, focus sounds, large hit targets, gamepad-first controls. Built as a separate SPA sharing the same data as the main UI — no extra deploy, just open `/console`. [Read more →](../using/console-mode.md) +A brand-new `/console` UI designed for TVs and gamepads. Spatial navigation, focus sounds, large hit targets, gamepad-first controls. Built as a separate SPA sharing the same data as the main UI, with no extra deploy: just open `/console`. [Read more →](../using/console-mode.md) ### Smart & Virtual Collections **Standard** collections (curated by you) now join two new flavours: -- **Smart Collections** — rule-based, auto-populating. Define a query, RomM keeps it in sync. -- **Virtual Collections** — auto-generated by RomM by genre, developer, year, or tag. +- **Smart Collections**: rule-based, auto-populating. Define a query, RomM keeps it in sync. +- **Virtual Collections**: auto-generated by RomM by genre, developer, year, or tag. [Collections → ](../using/collections.md) @@ -31,7 +31,7 @@ Apply IPS / UPS / BPS / PPF / RUP / APS / BDF / PMSR / vcdiff patches in the bro ### Netplay -Multiplayer for EmulatorJS sessions — host or join rooms, with ICE server configuration for NAT traversal. [Netplay →](../using/netplay.md) +Multiplayer for EmulatorJS sessions: host or join rooms, with ICE server configuration for NAT traversal. [Netplay →](../using/netplay.md) ### PWA install @@ -43,7 +43,7 @@ English (US/GB), Spanish, French, German, Italian, Portuguese (BR), Japanese, Ko ### Time to Beat & RetroAchievements tabs -HowLongToBeat completion times and RetroAchievements progression now surface as dedicated tabs on every game's detail page. Both need provider credentials — see [Metadata Providers](../administration/metadata-providers.md). +HowLongToBeat completion times and RetroAchievements progression now surface as dedicated tabs on every game's detail page. Both need provider credentials; see [Metadata Providers](../administration/metadata-providers.md). ### Bulk downloads, QR codes, copy-link @@ -63,8 +63,8 @@ Up from nine. New in 5.0: **TheGamesDB**, **Libretro metadata**, **gamelist.xml Two official image variants: -- **Full** (default) — includes EmulatorJS and Ruffle. ~1.2 GB. -- **Slim** — no in-browser emulators. ~400 MB. +- **Full** (default): includes EmulatorJS and Ruffle. ~1.2 GB. +- **Slim**: no in-browser emulators. ~400 MB. Pick based on whether you want in-browser play. [Image Variants →](../install/image-variants.md) @@ -101,7 +101,7 @@ First-class K8s manifest examples, including the mandatory `enableServiceLinks: ### Client API Tokens + device pairing -Per-user bearer tokens (max 25) with a subset of scopes, optionally paired to a device via a short code — so your handheld app doesn't have to type a 40-character secret. [Client API Tokens →](../ecosystem/client-api-tokens.md) +Per-user bearer tokens (max 25) with a subset of scopes, optionally paired to a device via a short code, so your handheld app doesn't have to type a 40-character secret. [Client API Tokens →](../ecosystem/client-api-tokens.md) ### Device Sync protocol @@ -123,10 +123,10 @@ Two endpoints: `/ws/socket.io` for general live updates (scan progress, notifica | Area | What changed | | --- | --- | -| **Database schema** | Alembic migrates automatically — but back up first. | +| **Database schema** | Alembic migrates automatically, but back up first. | | **Env var names** | A few renames around scheduled-task cron vars and OIDC role mapping. See [migration table](../releases/upgrading-to-5.0.md). | | **`config.yml`** | New sections for `scan.region`, `scan.language`, `scan.media`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings carry over. | -| **URL structure** | Docs URLs restructured (you're reading the new ones). Every old URL redirects — see [the redirect map](../releases/upgrading-to-5.0.md) in the migration guide. | +| **URL structure** | Docs URLs restructured (you're reading the new ones). Every old URL redirects; see [the redirect map](../releases/upgrading-to-5.0.md) in the migration guide. | | **Image tags** | `:slim` and `:5.0.0-slim` are new options alongside `:latest` and `:5.0.0`. | ## Where to go from here diff --git a/docs/index.md b/docs/index.md index 61b262fa..0eff3a11 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,13 +17,13 @@ Welcome to the **RomM Project**, the premier self-hosted, open source ROM manage Website · Demo · Discord
-RomM (ROM Manager) lets you scan, enrich, organise, and play your game collection from a clean web UI — with deep metadata from IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, LaunchBox, and more; in-browser play via EmulatorJS and Ruffle; companion apps for Android, handhelds, and desktop; and a first-class multi-user experience with OIDC SSO. +RomM (ROM Manager) lets you scan, enrich, organise, and play your game collection from a clean web UI, with deep metadata from IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, LaunchBox, and more; in-browser play via EmulatorJS and Ruffle; companion apps for Android, handhelds, and desktop; and a first-class multi-user experience with OIDC SSO. ## Where do you want to go?
-- :material-rocket-launch-outline: __I'm new — get me running__ +- :material-rocket-launch-outline: __I'm new, get me running__ --- diff --git a/docs/install/backup-and-restore.md b/docs/install/backup-and-restore.md index 6ccf9cd5..6dc667d6 100644 --- a/docs/install/backup-and-restore.md +++ b/docs/install/backup-and-restore.md @@ -5,18 +5,18 @@ description: Protect your RomM install against data loss, and move it between ho # Backup & Restore -This page covers both routine backups and migrating RomM to a new host — same procedure, different frequency. +This page covers both routine backups and migrating RomM to a new host: same procedure, different frequency. ## What to back up | Path / volume | What's in it | Backup? | | --- | --- | --- | -| **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical** — back this up nightly. | -| **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical** — back this up nightly. | -| **`/romm/config`** | `config.yml` and any custom overrides | **Critical** — rarely changes, but small and painful to recreate. | -| `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority — can be re-downloaded on a rescan. Including it speeds up recovery. | -| `/redis-data` | Task queue state | Low priority — in-flight tasks only; lost tasks can be re-run. | -| **`/romm/library`** | Your ROM files | Back this up **separately** — it's your source data and you should already have a backup strategy for it independent of RomM. | +| **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical**: back this up nightly. | +| **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical**: back this up nightly. | +| **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes, but small and painful to recreate. | +| `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority; can be re-downloaded on a rescan. Including it speeds up recovery. | +| `/redis-data` | Task queue state | Low priority; in-flight tasks only; lost tasks can be re-run. | +| **`/romm/library`** | Your ROM files | Back this up **separately**; it's your source data and you should already have a backup strategy for it independent of RomM. | ## Routine backup @@ -69,7 +69,7 @@ rsync -a --delete /srv/romm/config/ "$DEST/config/" find "$DEST" -maxdepth 1 -name 'db-*.sql.gz' -mtime +14 -delete ``` -Offsite it however you already do — rclone to B2/S3, restic, borg, Proxmox Backup Server — RomM doesn't care. +Offsite it however you already do (rclone to B2/S3, restic, borg, Proxmox Backup Server); RomM doesn't care. ## Restore @@ -88,7 +88,7 @@ Offsite it however you already do — rclone to B2/S3, restic, borg, Proxmox Bac ### In place (oh no, something's broken) -Same steps, but skip step 1 — stop the stack, swap the DB dump back in, restart. Keep the dump you're about to overwrite: `mv current-broken.sql rollback.sql`. +Same steps, but skip step 1. Stop the stack, swap the DB dump back in, restart. Keep the dump you're about to overwrite: `mv current-broken.sql rollback.sql`. ## Moving RomM to a new host @@ -165,4 +165,4 @@ Before upgrading to a new RomM major version: 4. Start back up: `docker compose start`. 5. Pull the new image and upgrade. -If the upgrade blows up, restore the dump + snapshot. For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md) first — there are breaking changes. +If the upgrade blows up, restore the dump + snapshot. For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md) first; there are breaking changes. diff --git a/docs/install/databases.md b/docs/install/databases.md index c325b3f7..b7e53f10 100644 --- a/docs/install/databases.md +++ b/docs/install/databases.md @@ -12,9 +12,9 @@ RomM uses SQLAlchemy + Alembic for persistence. Four drivers are supported; pick | **MariaDB** (default, recommended) | `mariadb` | `mariadb:11` | `3306` | What the reference compose uses. Well-tested. | | **MySQL** | `mysql` | `mysql:8` | `3306` | Largely interchangeable with MariaDB for RomM. | | **PostgreSQL** | `postgresql` | `postgres:16` | `5432` | Supported. Use if you already run Postgres. | -| **SQLite** | `sqlite` | _(file on disk)_ | — | Dev/tiny deployments only. Not recommended for anything multi-user or larger than a few hundred games. | +| **SQLite** | `sqlite` | _(file on disk)_ | n/a | Dev/tiny deployments only. Not recommended for anything multi-user or larger than a few hundred games. | -RomM runs Alembic migrations automatically on startup — no manual step when upgrading. +RomM runs Alembic migrations automatically on startup; no manual step when upgrading. ## MariaDB (default) @@ -109,7 +109,7 @@ services: ## SQLite (not recommended) -Set `ROMM_DB_DRIVER=sqlite`. The DB file lives at `{ROMM_BASE_PATH}/database`. No separate container required — useful for a laptop demo or a one-user install on a low-power box. Don't use this for anything you care about: +Set `ROMM_DB_DRIVER=sqlite`. The DB file lives at `{ROMM_BASE_PATH}/database`. No separate container required; useful for a laptop demo or a one-user install on a low-power box. Don't use this for anything you care about: - No concurrent writers → scans and API calls block each other. - The file can corrupt if the container is killed mid-write. @@ -124,7 +124,7 @@ environment: - DB_QUERY_JSON={"ssl": "true", "connect_timeout": "5"} ``` -Exact keys depend on the driver — see SQLAlchemy / the driver's docs. +Exact keys depend on the driver; see SQLAlchemy / the driver's docs. ## Which should I pick? @@ -133,4 +133,4 @@ Exact keys depend on the driver — see SQLAlchemy / the driver's docs. - **Single-user laptop demo?** SQLite is fine; upgrade before adding anyone else. - **External managed DB?** Any of MariaDB / MySQL / Postgres. Point `DB_HOST` at it and configure TLS via `DB_QUERY_JSON`. -Don't switch DB drivers on a running install without a plan — migrating the data requires a dump + reload, covered in [Backup & Restore](backup-and-restore.md). +Don't switch DB drivers on a running install without a plan; migrating the data requires a dump + reload, covered in [Backup & Restore](backup-and-restore.md). diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index e3f43c8f..dfd31fce 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -5,13 +5,13 @@ description: Canonical Docker Compose reference for a production RomM 5.0 deploy # Docker Compose -The canonical way to run RomM is with Docker Compose. This page describes the full reference stack — the shorter happy-path walkthrough lives in [Quick Start](../getting-started/quick-start.md). +The canonical way to run RomM is with Docker Compose. This page describes the full reference stack; the shorter happy-path walkthrough lives in [Quick Start](../getting-started/quick-start.md). The RomM stack has three parts: -1. **`romm`** — the application container (FastAPI backend, Vue frontend, nginx, and an embedded Redis/Valkey worker). -2. **A database** — MariaDB by default; MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. -3. **Redis or Valkey** — required for background tasks, session storage, and queue coordination. In the default `full-image` this runs inside the RomM container; for production you'll typically split it out. See [Redis or Valkey](redis-or-valkey.md). +1. **`romm`**: the application container (FastAPI backend, Vue frontend, nginx, and an embedded Redis/Valkey worker). +2. **A database**: MariaDB by default; MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. +3. **Redis or Valkey**: required for background tasks, session storage, and queue coordination. In the default `full-image` this runs inside the RomM container; for production you'll typically split it out. See [Redis or Valkey](redis-or-valkey.md). ## Reference `docker-compose.yml` @@ -26,7 +26,7 @@ The RomM stack has three parts: | Field | Value | Notes | | --- | --- | --- | | `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`. Pin to a specific tag (`5.0.0`) for production. See [Image Variants](image-variants.md) for `slim` vs `full`. | -| `ports` | `80:8080` | Container listens on `8080`. Expose through a reverse proxy in production — see [Reverse Proxy](reverse-proxy.md). | +| `ports` | `80:8080` | Container listens on `8080`. Expose through a reverse proxy in production; see [Reverse Proxy](reverse-proxy.md). | | `volumes` | see below | RomM writes to four distinct paths inside the container. | | `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | @@ -34,16 +34,16 @@ The RomM stack has three parts: | Path inside container | Purpose | Backup priority | | --- | --- | --- | -| `/romm/library` | Your ROM files. Typically mounted **read-only**. | No — this is your source data, back it up separately. | +| `/romm/library` | Your ROM files. Typically mounted **read-only**. | No. This is your source data, back it up separately. | | `/romm/assets` | User uploads: saves, states, uploaded screenshots. | **Critical.** Back this up with your DB. | -| `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low — can be re-downloaded on a rescan. | +| `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low; can be re-downloaded on a rescan. | | `/romm/config` | Holds `config.yml`. | **Critical.** Hand-tuned config, back it up. | See [Backup & Restore](backup-and-restore.md) for the full procedure. #### Core environment variables -Minimal set — see the [Environment Variables reference](../reference/environment-variables.md) for the complete list. +Minimal set; see the [Environment Variables reference](../reference/environment-variables.md) for the complete list. | Variable | What it does | | --- | --- | @@ -58,7 +58,7 @@ Minimal set — see the [Environment Variables reference](../reference/environme | Field | Value | Notes | | --- | --- | --- | | `image` | `mariadb:latest` | Pin to a major version (`mariadb:11`) for production. | -| `volumes` | `mysql_data:/var/lib/mysql` | Back this up — it's your entire catalogue. | +| `volumes` | `mysql_data:/var/lib/mysql` | Back this up; it's your entire catalogue. | | `healthcheck` | `healthcheck.sh --connect --innodb_initialized` | RomM waits for this. | The default compose file uses MariaDB because it requires no extra driver configuration. To use PostgreSQL, swap the image for `postgres:16`, set `ROMM_DB_DRIVER=postgresql`, and point `DB_PORT` at `5432`. See [Databases](databases.md) for the full swap. @@ -67,7 +67,7 @@ The default compose file uses MariaDB because it requires no extra driver config The quick-start compose file is functional but not production-ready. Before exposing RomM to the internet: -- **Pin image tags.** `rommapp/romm:latest` moves — use `rommapp/romm:5.0.0` (or whatever release you're on). +- **Pin image tags.** `rommapp/romm:latest` moves, so use `rommapp/romm:5.0.0` (or whatever release you're on). - **Use a reverse proxy with HTTPS.** The built-in nginx listens on `8080` and terminates plain HTTP. Put Traefik, Caddy, or nginx in front with TLS. See [Reverse Proxy](reverse-proxy.md). - **Split out Redis/Valkey.** The `full-image` embeds Redis; for production run a dedicated container and set `REDIS_HOST`. See [Redis or Valkey](redis-or-valkey.md). - **Set a non-default `MARIADB_ROOT_PASSWORD`.** And don't reuse it for `MARIADB_PASSWORD`. @@ -82,4 +82,4 @@ docker compose pull docker compose up -d ``` -Alembic migrations run automatically on startup. Read the [release notes](../releases/index.md) before every major-version bump — the 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). +Alembic migrations run automatically on startup. Read the [release notes](../releases/index.md) before every major-version bump; the 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). diff --git a/docs/install/image-variants.md b/docs/install/image-variants.md index 8934fe76..6c88d51c 100644 --- a/docs/install/image-variants.md +++ b/docs/install/image-variants.md @@ -5,7 +5,7 @@ description: Choose between the slim and full RomM container images. # Image Variants -RomM publishes two production image variants. They're interchangeable at the config level — pick based on whether you want in-browser emulation. +RomM publishes two production image variants. They're interchangeable at the config level; pick based on whether you want in-browser emulation. | Variant | Tag | Includes | Approx size | When to pick | | --- | --- | --- | --- | --- | @@ -28,17 +28,17 @@ services: docker compose up -d ``` -No data migration is required — saves, states, metadata, and `config.yml` are the same across variants. +No data migration is required; saves, states, metadata, and `config.yml` are the same across variants. ## Dev images -`rommapp/romm:dev-slim` and `rommapp/romm:dev-full` track the `master` branch with hot-reload-friendly entrypoints. Useful for testing changes against a real library — not recommended for anything you care about. See [Development Setup](../developers/development-setup.md). +`rommapp/romm:dev-slim` and `rommapp/romm:dev-full` track the `master` branch with hot-reload-friendly entrypoints. Useful for testing changes against a real library; not recommended for anything you care about. See [Development Setup](../developers/development-setup.md). ## What's actually different The two images share the same backend code, frontend bundle, nginx config, and entrypoint. The slim image omits: -- **EmulatorJS** (~700 MB uncompressed) — the in-browser retro emulator bundle. -- **Ruffle** (~20 MB) — the Flash/Shockwave emulator. +- **EmulatorJS** (~700 MB uncompressed): the in-browser retro emulator bundle. +- **Ruffle** (~20 MB): the Flash/Shockwave emulator. -If you set `ENABLE_EMULATORJS=false` and `ENABLE_RUFFLE=false` on the full image, behaviour matches slim — only the image is larger. Setting them to `true` on the slim image will cause players to 404 at runtime. +If you set `ENABLE_EMULATORJS=false` and `ENABLE_RUFFLE=false` on the full image, behaviour matches slim; only the image is larger. Setting them to `true` on the slim image will cause players to 404 at runtime. diff --git a/docs/install/index.md b/docs/install/index.md index 78146895..d577586d 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -1,6 +1,6 @@ --- title: Install & Deploy -description: Pick a deployment path — Docker Compose, Unraid, Synology, TrueNAS, or Kubernetes. +description: Pick a deployment path: Docker Compose, Unraid, Synology, TrueNAS, or Kubernetes. --- # Install & Deploy @@ -11,11 +11,11 @@ RomM is distributed as a Docker image. Every supported deployment runs the same | If you're on… | Start here | | --- | --- | -| A Linux server / NAS with Docker | **[Docker Compose](docker-compose.md)** — the canonical reference setup. | -| **Unraid** | [Unraid](unraid.md) — two supported paths (Community Apps template, Docker Compose Manager). | -| **Synology** | [Synology](synology.md) — Container Manager + DSM-specific notes. | -| **TrueNAS SCALE** | [TrueNAS](truenas.md) — App Catalog or YAML install. | -| **Kubernetes** | [Kubernetes](kubernetes.md) — manifest examples, required quirks. | +| A Linux server / NAS with Docker | **[Docker Compose](docker-compose.md)**: the canonical reference setup. | +| **Unraid** | [Unraid](unraid.md): two supported paths (Community Apps template, Docker Compose Manager). | +| **Synology** | [Synology](synology.md): Container Manager + DSM-specific notes. | +| **TrueNAS SCALE** | [TrueNAS](truenas.md): App Catalog or YAML install. | +| **Kubernetes** | [Kubernetes](kubernetes.md): manifest examples, required quirks. | If none of those match, start with [Docker Compose](docker-compose.md) and adapt. @@ -23,24 +23,24 @@ If none of those match, start with [Docker Compose](docker-compose.md) and adapt Regardless of host platform, you'll make the same handful of decisions: -- **[Image variant](image-variants.md)** — `full` (default, includes in-browser emulators) or `slim` (headless, smaller footprint). -- **[Database](databases.md)** — MariaDB (default), MySQL, PostgreSQL, or SQLite (dev-only). -- **[Redis / Valkey](redis-or-valkey.md)** — required for sessions + task queue. Embedded or external. -- **[Reverse proxy](reverse-proxy.md)** — Caddy, nginx, Traefik, or NPM. HTTPS required for OIDC and PWA install. -- **[Backup & restore](backup-and-restore.md)** — don't skip. Test the restore before you need it. +- **[Image variant](image-variants.md)**: `full` (default, includes in-browser emulators) or `slim` (headless, smaller footprint). +- **[Database](databases.md)**: MariaDB (default), MySQL, PostgreSQL, or SQLite (dev-only). +- **[Redis / Valkey](redis-or-valkey.md)**: required for sessions + task queue. Embedded or external. +- **[Reverse proxy](reverse-proxy.md)**: Caddy, nginx, Traefik, or NPM. HTTPS required for OIDC and PWA install. +- **[Backup & restore](backup-and-restore.md)**: don't skip. Test the restore before you need it. ## Minimum reading before going live -1. [Quick Start](../getting-started/quick-start.md) — 15-minute happy-path walkthrough. -2. [Docker Compose](docker-compose.md) — production-oriented reference compose. -3. [Reverse Proxy](reverse-proxy.md) — pick a TLS-terminating proxy. -4. [Backup & Restore](backup-and-restore.md) — nightly backup + restore drill. +1. [Quick Start](../getting-started/quick-start.md): 15-minute happy-path walkthrough. +2. [Docker Compose](docker-compose.md): production-oriented reference compose. +3. [Reverse Proxy](reverse-proxy.md): pick a TLS-terminating proxy. +4. [Backup & Restore](backup-and-restore.md): nightly backup + restore drill. Then come back here when you're ready to pick a platform-specific guide, or read [Administration](../administration/index.md) to set up your first users, metadata, and scheduled tasks. ## After you're up and running -- **Populate the library** — [Your First Scan](../getting-started/first-scan.md). -- **Add users** — [Invitations & Registration](../administration/invitations-and-registration.md). -- **Configure metadata providers** — [Metadata Providers](../administration/metadata-providers.md). -- **Upgrading from 4.x?** — [Upgrading to 5.0](../releases/upgrading-to-5.0.md). +- **Populate the library**: [Your First Scan](../getting-started/first-scan.md). +- **Add users**: [Invitations & Registration](../administration/invitations-and-registration.md). +- **Configure metadata providers**: [Metadata Providers](../administration/metadata-providers.md). +- **Upgrading from 4.x?** [Upgrading to 5.0](../releases/upgrading-to-5.0.md). diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index deca9543..ef9ecd08 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -1,6 +1,6 @@ --- title: Kubernetes -description: Deploy RomM on Kubernetes — manifest examples, required quirks, and community Helm charts. +description: Deploy RomM on Kubernetes with manifest examples, required quirks, and community Helm charts. --- # Kubernetes @@ -8,18 +8,18 @@ description: Deploy RomM on Kubernetes — manifest examples, required quirks, a There's no official Helm chart for RomM in 5.0. This page walks through a production-grade manifest set and points at the community charts worth looking at. !!! note "Community-maintained charts" - If you'd rather not hand-author manifests, there are community Helm charts around — check [ArtifactHub](https://artifacthub.io/packages/search?ts_query_web=romm) and the `#kubernetes` channel in the [RomM Discord](https://discord.gg/P5HtHnhUDH). The RomM team doesn't publish or formally support any chart today — pick one with active maintenance and recent releases. + If you'd rather not hand-author manifests, there are community Helm charts around; check [ArtifactHub](https://artifacthub.io/packages/search?ts_query_web=romm) and the `#kubernetes` channel in the [RomM Discord](https://discord.gg/P5HtHnhUDH). The RomM team doesn't publish or formally support any chart today, so pick one with active maintenance and recent releases. ## What you need - A cluster running Kubernetes 1.27+. -- Persistent storage for the DB, cache, and RomM's assets/resources/config (block or RWX — see below). +- Persistent storage for the DB, cache, and RomM's assets/resources/config (block or RWX; see below). - An Ingress controller (nginx-ingress, Traefik, etc.) for external access. - cert-manager or equivalent for HTTPS. ## Required quirk: `enableServiceLinks: false` -Kubernetes injects service addresses as environment variables into every pod — `HOSTNAME_PORT=tcp://:`. nginx inside the RomM image picks these up and tries to bind to the service IP, producing: +Kubernetes injects service addresses as environment variables into every pod (`HOSTNAME_PORT=tcp://:`). nginx inside the RomM image picks these up and tries to bind to the service IP, producing: ```text invalid host in "tcp://:8080" of the "listen" directive in @@ -40,7 +40,7 @@ spec: enableServiceLinks: false # ← required ``` -Without this, RomM crashloops on startup. This is the single most common Kubernetes gotcha — if you're here because of the error above, add the flag and move on. +Without this, RomM crashloops on startup. This is the single most common Kubernetes gotcha. If you're here because of the error above, add the flag and move on. ## Namespace @@ -64,7 +64,7 @@ stringData: ROMM_AUTH_SECRET_KEY: "" DB_PASSWD: "" MARIADB_ROOT_PASSWORD: "" - # Metadata providers — fill in only what you've configured: + # Metadata providers: fill in only what you've configured: IGDB_CLIENT_ID: "" IGDB_CLIENT_SECRET: "" SCREENSCRAPER_USER: "" @@ -271,13 +271,13 @@ spec: port: { number: 80 } ``` -`proxy-body-size: "0"` is important — the default is 1 MB and will reject every ROM upload with HTTP 413. +`proxy-body-size: "0"` is important; the default is 1 MB and will reject every ROM upload with HTTP 413. ## Scaling notes - **One RomM replica** is the simple path. The scan runs as a single worker, which prefers a single replica. - **Multiple replicas** work, but you need RWX for `/romm/assets` and `/romm/resources` and an external Redis (set `REDIS_HOST` to a shared service). See [Redis or Valkey](redis-or-valkey.md). -- **No HPA** (horizontal pod autoscaler) on RomM — CPU spikes during scans are normal and not a scaling signal. +- **No HPA** (horizontal pod autoscaler) on RomM: CPU spikes during scans are normal and not a scaling signal. ## Updating diff --git a/docs/install/redis-or-valkey.md b/docs/install/redis-or-valkey.md index ac9bd159..f5b7ca36 100644 --- a/docs/install/redis-or-valkey.md +++ b/docs/install/redis-or-valkey.md @@ -7,18 +7,18 @@ description: Why RomM needs Redis, how it's run, and when to split it out. RomM needs a Redis-protocol server. It's used for: -- **Session storage** — login sessions, CSRF tokens. -- **Background task queue** — scans, metadata syncs, sync operations run through RQ (Redis Queue). -- **Cache** — metadata lookups, heartbeat data, paired-device tokens, rate-limit counters. -- **Socket.IO** — multi-instance pubsub for live updates (only relevant if you're running more than one RomM container). +- **Session storage**: login sessions, CSRF tokens. +- **Background task queue**: scans, metadata syncs, sync operations run through RQ (Redis Queue). +- **Cache**: metadata lookups, heartbeat data, paired-device tokens, rate-limit counters. +- **Socket.IO**: multi-instance pubsub for live updates (only relevant if you're running more than one RomM container). -Both **Redis** and **Valkey** (the open-source Redis fork maintained by the Linux Foundation after Redis Inc.'s license change) are supported. They're interchangeable — Valkey is a drop-in wire-compatible replacement. Pick either. +Both **Redis** and **Valkey** (the open-source Redis fork maintained by the Linux Foundation after Redis Inc.'s license change) are supported. They're interchangeable: Valkey is a drop-in wire-compatible replacement. Pick either. ## Option A: embedded (default) The default `full-image` container runs a Redis server inside itself. Zero config, fine for single-instance deployments. The `REDIS_HOST` / `REDIS_PORT` defaults (`localhost:6379`) point at the embedded instance; leave them alone. -**Data** is persisted to `/redis-data` inside the container. In the reference compose, that's mounted to a Docker volume (`romm_redis_data`) so queue state survives restarts. Don't skip the volume — you'll lose in-flight tasks on every `docker compose up -d`. +**Data** is persisted to `/redis-data` inside the container. In the reference compose, that's mounted to a Docker volume (`romm_redis_data`) so queue state survives restarts. Don't skip the volume; you'll lose in-flight tasks on every `docker compose up -d`. ## Option B: external Redis container @@ -81,12 +81,12 @@ The full list of Redis env vars lives in [Environment Variables](../reference/en ## Tuning -RomM's Redis usage is light — sessions, a queue, a bit of cache. Defaults are fine for anything up to tens of thousands of ROMs and a few dozen users. Things to know: +RomM's Redis usage is light: sessions, a queue, a bit of cache. Defaults are fine for anything up to tens of thousands of ROMs and a few dozen users. Things to know: - **`appendonly yes`** is strongly recommended (what the reference compose uses). Without it, a crash loses any task currently in the queue. - **RDB snapshotting** is fine on top; `save 60 1` gives you a minutely snapshot. - **Memory**: RomM doesn't hold large blobs in Redis. A 256 MB `maxmemory` is plenty for most instances. -- **Key eviction**: leave `maxmemory-policy` alone (default: `noeviction`). RomM doesn't tolerate silent key loss — sessions would drop and running tasks would lose state. +- **Key eviction**: leave `maxmemory-policy` alone (default: `noeviction`). RomM doesn't tolerate silent key loss: sessions would drop and running tasks would lose state. ## Verifying it works @@ -98,7 +98,7 @@ docker exec romm redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWD" If you see `PONG` from the RomM container, you're good. If not, check: - That the DNS name in `REDIS_HOST` resolves from the RomM container (use `docker exec romm getent hosts romm-redis`). -- That the password is correct — `redis-cli -a` will say `NOAUTH` if wrong. +- That the password is correct; `redis-cli -a` will say `NOAUTH` if wrong. - That `REDIS_SSL=true` matches whether the server actually requires TLS. Debugging further: see the Redis line in `docker logs romm` at startup; RomM logs the connection target. diff --git a/docs/install/reverse-proxy.md b/docs/install/reverse-proxy.md index 655ff4cb..c855107e 100644 --- a/docs/install/reverse-proxy.md +++ b/docs/install/reverse-proxy.md @@ -8,7 +8,7 @@ description: Put RomM behind Caddy, nginx, Traefik, or Nginx Proxy Manager with The RomM container listens on plain HTTP on port `8080`. For anything beyond `localhost` you should put it behind a reverse proxy that terminates TLS and forwards to the container. !!! tip "WebSockets are required" - RomM uses Socket.IO (both the general `/ws/socket.io` endpoint and the `/netplay/socket.io` endpoint) for live updates, scan progress, and Netplay. Every reverse-proxy recipe below keeps WebSocket support on — don't strip it out. + RomM uses Socket.IO (both the general `/ws/socket.io` endpoint and the `/netplay/socket.io` endpoint) for live updates, scan progress, and Netplay. Every reverse-proxy recipe below keeps WebSocket support on; don't strip it out. The examples here assume your RomM container is reachable at `romm:8080` (by container name on a Docker network) or `192.168.1.100:8080` (by IP on the LAN). Swap to whatever's right for your setup. @@ -147,7 +147,7 @@ labels: ## Nginx Proxy Manager -Items marked ❗ are important — RomM won't work right without them. +Items marked ❗ are important; RomM won't work right without them. ### Details @@ -169,7 +169,7 @@ Items marked ❗ are important — RomM won't work right without them. - **Email Address for Let's Encrypt**: your address - **I Agree to the TOS**: `on` -### Advanced — custom nginx configuration ❗ +### Advanced: custom nginx configuration ❗ ```nginx proxy_max_temp_file_size 0; @@ -190,4 +190,4 @@ environment: - ROMM_BASE_URL=https://romm.mysite.com ``` -If you're also using OIDC, update `OIDC_REDIRECT_URI` to match — see [OIDC Setup](../administration/oidc/index.md). +If you're also using OIDC, update `OIDC_REDIRECT_URI` to match; see [OIDC Setup](../administration/oidc/index.md). diff --git a/docs/install/synology.md b/docs/install/synology.md index 5581d072..0cddc64a 100644 --- a/docs/install/synology.md +++ b/docs/install/synology.md @@ -31,7 +31,7 @@ mkdir -p /volume1/data/media/games/library/roms mkdir -p /volume1/data/media/games/library/bios ``` -The platform folder names inside `roms/` have to match RomM's conventions — see [Folder Structure](../getting-started/folder-structure.md). +The platform folder names inside `roms/` have to match RomM's conventions; see [Folder Structure](../getting-started/folder-structure.md). ### User uploads + config @@ -51,7 +51,7 @@ mkdir -p /volume1/docker/mariadb-romm ## 2. Create a bridge network -RomM and MariaDB need to reach each other by container name. Create a Docker bridge named `rommbridge` — [guide here](https://drfrankenstein.co.uk/step-3-setting-up-a-docker-bridge-network-in-container-manager/). +RomM and MariaDB need to reach each other by container name. Create a Docker bridge named `rommbridge`: [guide here](https://drfrankenstein.co.uk/step-3-setting-up-a-docker-bridge-network-in-container-manager/). ## 3. Generate the auth secret @@ -60,7 +60,7 @@ openssl rand -hex 32 # -> 03a054b6ca27e0107c5eed552ea66bacd9f3a2a8a91e7595cd462a593f9ecd09 ``` -Keep the output — it becomes `ROMM_AUTH_SECRET_KEY` in your compose file. Don't lose it; rotating invalidates every session and invite link. +Keep the output; it becomes `ROMM_AUTH_SECRET_KEY` in your compose file. Don't lose it; rotating invalidates every session and invite link. ## 4. Set up metadata provider credentials @@ -69,9 +69,9 @@ Recommended before the first scan. Full walkthrough in [Metadata Providers](../a ## 5. Docker Compose !!! info "MariaDB 10.7 note" - This guide pins MariaDB to **10.7** for stability on older DSM versions. MariaDB 11 works on DSM 7.2+ — bump the image tag if you like. + This guide pins MariaDB to **10.7** for stability on older DSM versions. MariaDB 11 works on DSM 7.2+; bump the image tag if you like. -The Synology-flavoured compose file — MariaDB on port `3309` externally (to avoid colliding with Synology's built-in MariaDB), UID/GID customisation, simplified healthcheck: +The Synology-flavoured compose file: MariaDB on port `3309` externally (to avoid colliding with Synology's built-in MariaDB), UID/GID customisation, simplified healthcheck: ???+ example "docker-compose.yml" ``` yaml @@ -88,7 +88,7 @@ From the directory holding your compose file: sudo docker compose up -d ``` -**Be patient** — the first start takes a few minutes while MariaDB initialises, RomM runs migrations, and resources get seeded. Tail the logs: +**Be patient.** The first start takes a few minutes while MariaDB initialises, RomM runs migrations, and resources get seeded. Tail the logs: ```bash sudo docker compose logs -f @@ -106,9 +106,9 @@ Once RomM reports it's listening, open `http://:7676` in a browser. The Common Synology gotchas: -- **"Page not found" on first open** — DSM hit RomM before it finished first-run init. Wait for `docker compose logs -f` to calm down. -- **Database connection errors** — check the MariaDB container is healthy (`docker ps` → status `healthy`), and that RomM's `DB_HOST` matches the MariaDB service name in compose. -- **Permission errors on assets/resources folders** — verify the UID/GID in the compose matches the owner of those host paths on the NAS (`ls -la /volume1/data/media/games/`). +- **"Page not found" on first open**: DSM hit RomM before it finished first-run init. Wait for `docker compose logs -f` to calm down. +- **Database connection errors**: check the MariaDB container is healthy (`docker ps` → status `healthy`), and that RomM's `DB_HOST` matches the MariaDB service name in compose. +- **Permission errors on assets/resources folders**: verify the UID/GID in the compose matches the owner of those host paths on the NAS (`ls -la /volume1/data/media/games/`). Synology-specific problems that come up often: [Synology Troubleshooting](../troubleshooting/synology.md). diff --git a/docs/install/truenas.md b/docs/install/truenas.md index 044ac211..6ce0a873 100644 --- a/docs/install/truenas.md +++ b/docs/install/truenas.md @@ -5,7 +5,7 @@ description: Install RomM on TrueNAS SCALE via the App Catalog or YAML. # TrueNAS -This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported — its FreeBSD jail system doesn't run Docker images. +This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported; its FreeBSD jail system doesn't run Docker images. ## Prerequisites @@ -13,7 +13,7 @@ This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported — its FreeBS - Your ROM library arranged in the expected [folder structure](../getting-started/folder-structure.md). - A TrueNAS user/group with a UID/GID that can own the dataset paths you'll mount into RomM. -## Option A — App Catalog (recommended) +## Option A: App Catalog (recommended) ### 1. Open the RomM app @@ -23,12 +23,12 @@ This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported — its FreeBS ### 2. Fill in the install form -Step through the installation UI. You'll be asked for the same set of env vars as [Quick Start](../getting-started/quick-start.md) — most defaults work. Things to look at: +Step through the installation UI. You'll be asked for the same set of env vars as [Quick Start](../getting-started/quick-start.md); most defaults work. Things to look at: -- **Database credentials** — TrueNAS will offer to provision MariaDB for you; just pick a strong password. -- **`ROMM_AUTH_SECRET_KEY`** — generate via `openssl rand -hex 32` on any Linux box and paste the output. -- **Metadata provider credentials** — fill in whatever you've registered for. See [Metadata Providers](../administration/metadata-providers.md). -- **Storage configurations** — point the **Library** and **Assets** volumes at datasets you control. Make sure the UID/GID defined in the app config (default: `568` — the `apps` user) has ACL access to those datasets. +- **Database credentials**: TrueNAS will offer to provision MariaDB for you; just pick a strong password. +- **`ROMM_AUTH_SECRET_KEY`**: generate via `openssl rand -hex 32` on any Linux box and paste the output. +- **Metadata provider credentials**: fill in whatever you've registered for. See [Metadata Providers](../administration/metadata-providers.md). +- **Storage configurations**: point the **Library** and **Assets** volumes at datasets you control. Make sure the UID/GID defined in the app config (default: `568`, the `apps` user) has ACL access to those datasets. ![RomM Library Example](../resources/truenas/app-config.png) @@ -36,7 +36,7 @@ Step through the installation UI. You'll be asked for the same set of env vars a Save. TrueNAS provisions the container + DB + Redis, runs migrations, and exposes the web UI on the port you configured. If it won't boot, jump to [Troubleshooting](#troubleshooting). -## Option B — Install via YAML +## Option B: Install via YAML Use this path when the App Catalog has a bug, or when you want more flexibility than the install UI exposes. @@ -57,7 +57,7 @@ Fill in the empty values with credentials you created in [Quick Start](../gettin ### 3. Install -Save. Same troubleshooting applies — see below. +Save. Same troubleshooting applies; see below. ## Troubleshooting @@ -81,4 +81,4 @@ In at least one reported setup, creating a user/group in TrueNAS with UID/GID `1 ## Contributing -Suggestions welcome — PRs against [rommapp/docs](https://github.com/rommapp/docs). +Suggestions welcome; PRs against [rommapp/docs](https://github.com/rommapp/docs). diff --git a/docs/install/unraid.md b/docs/install/unraid.md index f1be2b18..9aca26d6 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -7,8 +7,8 @@ description: Install RomM on Unraid via the Community Apps template or the Docke Two supported install paths on Unraid. Pick one: -- **[Community Apps template](#community-apps-template)** — simplest. Install RomM and MariaDB as separate CA templates. Good for users who already manage containers one-at-a-time. -- **[Docker Compose Manager](#docker-compose-manager)** — closer to the upstream reference setup. Drops the standard `docker-compose.yml` in and uses the Compose plugin to manage it. Recommended if you're comfortable editing Compose files and want parity with other deployments. +- **[Community Apps template](#community-apps-template)**: simplest. Install RomM and MariaDB as separate CA templates. Good for users who already manage containers one-at-a-time. +- **[Docker Compose Manager](#docker-compose-manager)**: closer to the upstream reference setup. Drops the standard `docker-compose.yml` in and uses the Compose plugin to manage it. Recommended if you're comfortable editing Compose files and want parity with other deployments. Both end up with the same running stack. @@ -33,11 +33,11 @@ Both end up with the same running stack. ### 1. Install MariaDB -From **Apps** → search `mariadb`. Only the [official `mariadb`](https://hub.docker.com/_/mariadb) and [linuxserver/docker-mariadb](https://github.com/linuxserver/docker-mariadb/pkgs/container/mariadb) templates are supported — **prefer the official one**. +From **Apps** → search `mariadb`. Only the [official `mariadb`](https://hub.docker.com/_/mariadb) and [linuxserver/docker-mariadb](https://github.com/linuxserver/docker-mariadb/pkgs/container/mariadb) templates are supported. **Prefer the official one.** ![community apps search results for MariaDB](https://github.com/user-attachments/assets/76f4b6ef-5b63-454f-9357-d2920b9afd0e) -Fill in the env vars — names and sensible defaults live in the [reference `docker-compose.yml`](docker-compose.md). Set the network to **Custom: romm**. +Fill in the env vars; names and sensible defaults live in the [reference `docker-compose.yml`](docker-compose.md). Set the network to **Custom: romm**. ![MariaDB environment variables](https://github.com/user-attachments/assets/a11906c5-25b2-46f1-906b-451a9ee39dca) @@ -46,7 +46,7 @@ Fill in the env vars — names and sensible defaults live in the [reference `doc ### 2. Install RomM -From **Apps** → search `romm` → install the app labelled **OFFICIAL** (maintained by the RomM team, always current). +From **Apps**, search `romm`, install the app labelled **OFFICIAL** (maintained by the RomM team, always current). ![RomM official app](https://github.com/user-attachments/assets/57c4d47a-8604-4e8d-b05a-84dd68dda124) @@ -83,7 +83,7 @@ Click the gear icon → **Edit Stack** → **Edit Compose**. ![Edit Stack](../resources/unraid/edit-stack.png) -Paste the [reference `docker-compose.yml`](docker-compose.md) and fill in your env vars (API keys, MariaDB creds, metadata providers). You can keep secrets in a separate `.env` file — edit the environment file via the gear icon. +Paste the [reference `docker-compose.yml`](docker-compose.md) and fill in your env vars (API keys, MariaDB creds, metadata providers). You can keep secrets in a separate `.env` file: edit the environment file via the gear icon. ![Edit Compose](../resources/unraid/unraid-compose.png) @@ -92,7 +92,7 @@ Paste the [reference `docker-compose.yml`](docker-compose.md) and fill in your e Save after each edit. !!! warning "Folder structure" - Make sure your library matches one of the [supported folder layouts](../getting-started/folder-structure.md) before scanning — Unraid users often forget this step. + Make sure your library matches one of the [supported folder layouts](../getting-started/folder-structure.md) before scanning. Unraid users often forget this step. ### 2. Bring it up @@ -100,7 +100,7 @@ Click **Compose Up**. ![Compose Up Working](../resources/unraid/docker-compose-loading.png) -Copy `IP:Port` from the RomM container and open it in a browser — the first-run Setup Wizard should appear. +Copy `IP:Port` from the RomM container and open it in a browser. The first-run Setup Wizard should appear. ![Compose Up](../resources/unraid/docker-compose-up.png) @@ -112,17 +112,17 @@ Copy `IP:Port` from the RomM container and open it in a browser — the first-ru Community-made, still relevant for general Unraid/RomM debugging even if specific UI screens have drifted. -[DemonWarriorTech](https://www.youtube.com/@DemonWarriorTech) — [How to Install RomM on Unraid (Beginner Friendly)](https://www.youtube.com/watch?v=Oo5obHNy2iw): +[DemonWarriorTech](https://www.youtube.com/@DemonWarriorTech): [How to Install RomM on Unraid (Beginner Friendly)](https://www.youtube.com/watch?v=Oo5obHNy2iw): -[![RomM on Unraid — DemonWarriorTech](https://img.youtube.com/vi/Oo5obHNy2iw/0.jpg)](https://www.youtube.com/watch?v=Oo5obHNy2iw) +[![RomM on Unraid, DemonWarriorTech](https://img.youtube.com/vi/Oo5obHNy2iw/0.jpg)](https://www.youtube.com/watch?v=Oo5obHNy2iw) -[AlienTech42](https://www.youtube.com/@AlienTech42) — [older install + debugging walkthrough](https://www.youtube.com/watch?v=ls5YcsFdwLQ). Install steps are out of date, but the general-setup and debugging portions still hold. +[AlienTech42](https://www.youtube.com/@AlienTech42): [older install + debugging walkthrough](https://www.youtube.com/watch?v=ls5YcsFdwLQ). Install steps are out of date, but the general-setup and debugging portions still hold. -[![RomM on Unraid — AlienTech42](https://img.youtube.com/vi/ls5YcsFdwLQ/0.jpg)](https://www.youtube.com/watch?v=ls5YcsFdwLQ) +[![RomM on Unraid, AlienTech42](https://img.youtube.com/vi/ls5YcsFdwLQ/0.jpg)](https://www.youtube.com/watch?v=ls5YcsFdwLQ) ## Unraid community support -Dedicated [Unraid forums support thread](https://forums.unraid.net/topic/149738-support-eurotimmy-romm-rom-manager-by-zurdi15/) — good for Unraid-specific questions that aren't in the RomM Discord's Unraid channel. +Dedicated [Unraid forums support thread](https://forums.unraid.net/topic/149738-support-eurotimmy-romm-rom-manager-by-zurdi15/): good for Unraid-specific questions that aren't in the RomM Discord's Unraid channel. ## Shout-outs diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index 6f68fbf0..d890a7f7 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -5,7 +5,7 @@ description: Add platforms RomM doesn't natively support, plus custom platform i # Custom Platforms -RomM ships with support for ~400 platforms. If yours isn't in [the list](supported-platforms.md), you can still load it as a custom platform — RomM just won't have metadata-provider coverage for it. +RomM ships with support for ~400 platforms. If yours isn't in [the list](supported-platforms.md), you can still load it as a custom platform; RomM just won't have metadata-provider coverage for it. ## Adding a custom platform @@ -27,7 +27,7 @@ Then either run a **Quick Scan** (the platform is auto-discovered) or trigger a ## Mapping to a canonical platform (preferred when possible) -If your platform is one RomM supports but under a different slug, you don't need a custom platform — just **remap**. Add to `config.yml`: +If your platform is one RomM supports but under a different slug, you don't need a custom platform; just **remap**. Add to `config.yml`: ```yaml system: @@ -58,7 +58,7 @@ services: ### 2. Seed it with the official icons -RomM's built-in icons live at [`frontend/assets/platforms`](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms). Download them all and drop into your mounted directory — otherwise built-in platforms lose their icons too (because your mount overrides the whole directory). +RomM's built-in icons live at [`frontend/assets/platforms`](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms). Download them all and drop into your mounted directory; otherwise built-in platforms lose their icons too (because your mount overrides the whole directory). ### 3. Add your custom `.ico` files @@ -70,7 +70,7 @@ The filename has to **match the IGDB platform slug**. Examples: | Pocket Challenge V2 | `pocket-challenge-v2` (custom) | `pocket-challenge-v2.ico` | | NES | `nes` | `nes.ico` | -Find the slug in the URL of the platform's IGDB page — e.g. [igdb.com/platforms/acpc](https://www.igdb.com/platforms/acpc) → slug is `acpc`. +Find the slug in the URL of the platform's IGDB page, e.g. [igdb.com/platforms/acpc](https://www.igdb.com/platforms/acpc) → slug is `acpc`. ### 4. Restart RomM @@ -78,7 +78,7 @@ Find the slug in the URL of the platform's IGDB page — e.g. [igdb.com/platform ### Replacing an existing platform's icon -Same mechanic — just use a filename matching an existing platform's slug and your file overrides the built-in. +Same mechanic: just use a filename matching an existing platform's slug and your file overrides the built-in. ## What custom platforms don't get @@ -90,7 +90,7 @@ For the niche platform case, you'll likely rely on filename-only matching (no pr ## Contributing a platform -If you've added a custom platform that should be supported natively by RomM — e.g. a widely-used platform that's missing — open an issue on [rommapp/romm](https://github.com/rommapp/romm/issues) with: +If you've added a custom platform that should be supported natively by RomM (e.g. a widely-used platform that's missing) open an issue on [rommapp/romm](https://github.com/rommapp/romm/issues) with: - Platform name. - IGDB platform URL (if it exists on IGDB). diff --git a/docs/platforms/emulatorjs-config.md b/docs/platforms/emulatorjs-config.md index 4e59ee1b..9ee767c7 100644 --- a/docs/platforms/emulatorjs-config.md +++ b/docs/platforms/emulatorjs-config.md @@ -1,11 +1,11 @@ --- title: EmulatorJS Configuration -description: Operator-level tuning of the EmulatorJS player — per-core settings, controls, cache, Netplay. +description: Operator-level tuning of the EmulatorJS player, per-core settings, controls, cache, Netplay. --- # EmulatorJS Configuration -End-user side of EmulatorJS lives in [In-Browser Play](../using/in-browser-play.md). This page is **operator-facing** — how to tune EmulatorJS via `config.yml` for every user on your instance. +End-user side of EmulatorJS lives in [In-Browser Play](../using/in-browser-play.md). This page is **operator-facing**: how to tune EmulatorJS via `config.yml` for every user on your instance. All settings below live under the `emulatorjs:` key in [`config.yml`](../reference/configuration-file.md#emulatorjs). @@ -16,7 +16,7 @@ environment: - ENABLE_EMULATORJS=true # default ``` -Set to `false` to disable EmulatorJS entirely — useful on the slim image or when running headless with companion apps only. The Play button is hidden; Ruffle still works independently. See [Image Variants](../install/image-variants.md). +Set to `false` to disable EmulatorJS entirely: useful on the slim image or when running headless with companion apps only. The Play button is hidden; Ruffle still works independently. See [Image Variants](../install/image-variants.md). ## Debug mode @@ -34,7 +34,7 @@ emulatorjs: cache_limit: 52428800 # 50 MB per ROM; null = unlimited ``` -Each ROM's cache (core files, BIOS, settings) is capped. `null` removes the cap — useful for large PSP / Saturn libraries on hosts with plenty of disk. +Each ROM's cache (core files, BIOS, settings) is capped. `null` removes the cap: useful for large PSP / Saturn libraries on hosts with plenty of disk. ## DOS-specific toggles @@ -44,8 +44,8 @@ emulatorjs: disable_auto_unload: true ``` -- **`disable_batch_bootup`** — skips the `autorun.bat` step when `dosbox-pure` loads. Try this if DOS games hang on boot. See [MS-DOS](ms-dos.md). -- **`disable_auto_unload`** — keeps the emulator running when the user navigates away. Default off (browsers unload the emulator on page change). +- **`disable_batch_bootup`**: skips the `autorun.bat` step when `dosbox-pure` loads. Try this if DOS games hang on boot. See [MS-DOS](ms-dos.md). +- **`disable_auto_unload`**: keeps the emulator running when the user navigates away. Default off (browsers unload the emulator on page change). ## Netplay @@ -127,7 +127,7 @@ The structure: - `value` = keyboard key. - `value2` = gamepad button. -Slot indexes and what they map to vary per core — see [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/). +Slot indexes and what they map to vary per core; see [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/). Users can override operator-level defaults in-game via Menu → **Controls**. Operator-level just sets the starting point. @@ -164,17 +164,17 @@ Per-user overrides take precedence. A config.yml setting is the fallback. For frontend parity: -- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml) — Batocera / RetroBat control + setting layouts. -- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) — ES-DE layout. +- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml): Batocera / RetroBat control + setting layouts. +- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml): ES-DE layout. ## Troubleshooting -- **Settings don't apply** — `debug: true`, reload a game, check console for "option set" logs. Core name might be wrong. -- **Netplay breaks after enabling** — see [Netplay Troubleshooting](../troubleshooting/netplay.md). -- **Control map works in one game, not another** — different cores; check you've set the right core name for the platform. +- **Settings don't apply.** `debug: true`, reload a game, check console for "option set" logs. Core name might be wrong. +- **Netplay breaks after enabling.** See [Netplay Troubleshooting](../troubleshooting/netplay.md). +- **Control map works in one game, not another.** Different cores; check you've set the right core name for the platform. ## See also -- [In-Browser Play](../using/in-browser-play.md) — end-user-facing. -- [Configuration File → `emulatorjs`](../reference/configuration-file.md#emulatorjs) — full schema. -- [EmulatorJS docs](https://emulatorjs.org/docs/) — upstream reference. +- [In-Browser Play](../using/in-browser-play.md): end-user-facing. +- [Configuration File → `emulatorjs`](../reference/configuration-file.md#emulatorjs): full schema. +- [EmulatorJS docs](https://emulatorjs.org/docs/): upstream reference. diff --git a/docs/platforms/firmware-by-platform.md b/docs/platforms/firmware-by-platform.md index 51881eea..b5888632 100644 --- a/docs/platforms/firmware-by-platform.md +++ b/docs/platforms/firmware-by-platform.md @@ -17,20 +17,20 @@ This page lists the firmware RomM's EmulatorJS cores need for each platform. For | **PlayStation (PSX)** | `ps` | `scph1001.bin` (US), `scph5501.bin` (US), `scph5502.bin` (EU), `scph7502.bin` (JP) | At least one region BIOS required. | | **Saturn** | `saturn` | `saturn_bios.bin`, optional `mpr-17933.bin` (EU) | Bundle both in a zip. | | **Sega CD / Mega CD** | `segacd` | `bios_CD_U.bin` (US), `bios_CD_E.bin` (EU), `bios_CD_J.bin` (JP) | Region depends on your games. | -| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional — games run without it, but accurate emulation needs it. | +| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional. Games run without it, but accurate emulation needs it. | | **Nintendo DS** | `nds` | `bios7.bin`, `bios9.bin`, `firmware.bin` | All three are needed. | -| **Atari Lynx** | `lynx` | `lynxboot.img` | — | +| **Atari Lynx** | `lynx` | `lynxboot.img` | | | **Neo Geo Pocket / Color** | `ngp` / `ngpc` | `neopop.rom` | Bundle as zip. | | **Amiga** | `amiga` | Kickstart ROMs (various) | `puae` core. Kickstart 1.2, 1.3, 3.1 depending on title. Bundle as zip. | -| **ColecoVision** | `colecovision` | `coleco.rom` | — | +| **ColecoVision** | `colecovision` | `coleco.rom` | | | **Intellivision** | `intellivision` | `exec.bin`, `grom.bin` | Bundle both. | -| **3DO** | `3do` | `panafz10.bin` (or similar region BIOS) | — | -| **PC Engine CD / TurboGrafx-CD** | `pcecd` / `tg16cd` | `syscard3.pce` | — | +| **3DO** | `3do` | `panafz10.bin` (or similar region BIOS) | | +| **PC Engine CD / TurboGrafx-CD** | `pcecd` / `tg16cd` | `syscard3.pce` | | | **Dreamcast** | `dc` | `dc_boot.bin`, `dc_flash.bin` | EmulatorJS Dreamcast support is limited; results vary. | -| **Atari 5200** | `atari5200` | `5200.rom` | — | -| **Atari 7800** | `atari7800` | `7800 BIOS (U).rom` | — | +| **Atari 5200** | `atari5200` | `5200.rom` | | +| **Atari 7800** | `atari7800` | `7800 BIOS (U).rom` | | -For cores + platforms not in this table — most Nintendo consoles (NES, SNES, N64), Sega Genesis, Atari 2600, Game Boy, most home computers — **no firmware is needed**. +For cores + platforms not in this table (most Nintendo consoles such as NES, SNES, N64, plus Sega Genesis, Atari 2600, Game Boy, most home computers), **no firmware is needed**. ## Bundling multi-file firmware @@ -53,11 +53,11 @@ Emulator cores look for **specific filenames**. If you rename `scph1001.bin` to Common mistakes: -- Upper vs lower case — most cores are case-sensitive. Use exact case from the table above. -- Extensions — `.bin` vs `.rom` vs `.img` depend on the core. Don't rename. -- Version suffixes — `scph1001 (US) v4.4.bin` won't load; it has to be `scph1001.bin`. +- Upper vs lower case: most cores are case-sensitive. Use exact case from the table above. +- Extensions: `.bin` vs `.rom` vs `.img` depend on the core. Don't rename. +- Version suffixes: `scph1001 (US) v4.4.bin` won't load; it has to be `scph1001.bin`. -If you're not sure what name a core expects, check the [upstream EmulatorJS systems docs](https://emulatorjs.org/docs/systems/) or load the game with `emulatorjs.debug: true` — the browser console will tell you what file it tried to load. +If you're not sure what name a core expects, check the [upstream EmulatorJS systems docs](https://emulatorjs.org/docs/systems/) or load the game with `emulatorjs.debug: true`; the browser console will tell you what file it tried to load. ## Getting firmware @@ -69,7 +69,7 @@ Your options (jurisdiction-dependent): ## Verifying integrity -Firmware is hash-verified by the emulator — a wrong hash means the file won't load. Quick CLI check: +Firmware is hash-verified by the emulator: a wrong hash means the file won't load. Quick CLI check: ```sh sha1sum scph1001.bin @@ -80,7 +80,7 @@ Known-good hashes for common BIOS files are documented on [No-Intro](https://dat ## See also -- [Firmware Management](../administration/firmware-management.md) — admin-side upload and lifecycle. -- [Supported Platforms](supported-platforms.md) — catalogue with firmware-required flag. -- [In-Browser Play Troubleshooting → "Firmware required"](../troubleshooting/in-browser-play.md#firmware-required) — diagnostic. -- [EmulatorJS systems reference](https://emulatorjs.org/docs/systems/) — upstream firmware requirements per core. +- [Firmware Management](../administration/firmware-management.md): admin-side upload and lifecycle. +- [Supported Platforms](supported-platforms.md): catalogue with firmware-required flag. +- [In-Browser Play Troubleshooting → "Firmware required"](../troubleshooting/in-browser-play.md#firmware-required): diagnostic. +- [EmulatorJS systems reference](https://emulatorjs.org/docs/systems/): upstream firmware requirements per core. diff --git a/docs/platforms/index.md b/docs/platforms/index.md index 6d08d7df..d5249799 100644 --- a/docs/platforms/index.md +++ b/docs/platforms/index.md @@ -1,18 +1,18 @@ --- title: Platforms & Players -description: Everything about the platforms RomM supports — catalogue, custom mappings, emulator tuning, firmware. +description: Everything about the platforms RomM supports: catalogue, custom mappings, emulator tuning, firmware. --- # Platforms & Players This section covers: -- **[Supported Platforms](supported-platforms.md)** — the canonical catalogue of every platform RomM ships with. Platform slugs, active metadata providers per platform, EmulatorJS support flag, and firmware requirements. -- **[Custom Platforms](custom-platforms.md)** — how to add platforms RomM doesn't know about, plus custom platform icons. -- **[MS-DOS](ms-dos.md)** — long-form guide to DOS on RomM: `dosbox-pure`, installer ROMs, autorun scripts, known issues. -- **[EmulatorJS Configuration](emulatorjs-config.md)** — operator-level tuning of the EmulatorJS player: per-core settings, control mappings, cache limits, Netplay. -- **[Ruffle Configuration](ruffle-config.md)** — Flash / Shockwave configuration. -- **[Firmware by Platform](firmware-by-platform.md)** — which firmware files each platform needs, file names, and where to get them legally. +- **[Supported Platforms](supported-platforms.md)**: the canonical catalogue of every platform RomM ships with. Platform slugs, active metadata providers per platform, EmulatorJS support flag, and firmware requirements. +- **[Custom Platforms](custom-platforms.md)**: how to add platforms RomM doesn't know about, plus custom platform icons. +- **[MS-DOS](ms-dos.md)**: long-form guide to DOS on RomM: `dosbox-pure`, installer ROMs, autorun scripts, known issues. +- **[EmulatorJS Configuration](emulatorjs-config.md)**: operator-level tuning of the EmulatorJS player: per-core settings, control mappings, cache limits, Netplay. +- **[Ruffle Configuration](ruffle-config.md)**: Flash / Shockwave configuration. +- **[Firmware by Platform](firmware-by-platform.md)**: which firmware files each platform needs, file names, and where to get them legally. ## Quick orientation @@ -23,6 +23,6 @@ This section covers: ## Related sections -- **[In-Browser Play](../using/in-browser-play.md)** — the end-user side of EmulatorJS + Ruffle. -- **[Metadata Providers](../administration/metadata-providers.md)** — which providers each platform has coverage from. -- **[Folder Structure](../getting-started/folder-structure.md)** — how the platform slug maps to on-disk folder names. +- **[In-Browser Play](../using/in-browser-play.md)**: the end-user side of EmulatorJS + Ruffle. +- **[Metadata Providers](../administration/metadata-providers.md)**: which providers each platform has coverage from. +- **[Folder Structure](../getting-started/folder-structure.md)**: how the platform slug maps to on-disk folder names. diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md index 133043a4..6b4ffbfd 100644 --- a/docs/platforms/ms-dos.md +++ b/docs/platforms/ms-dos.md @@ -1,13 +1,13 @@ --- title: MS-DOS -description: How to run DOS games in RomM via dosbox-pure — homebrew, shareware demos, and retail CDs. +description: How to run DOS games in RomM via dosbox-pure, homebrew, shareware demos, and retail CDs. --- # MS-DOS -DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-pure) — the EmulatorJS integration. Create a DOS platform (folder named `dos`) and drop your games in. +DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-pure), the EmulatorJS integration. Create a DOS platform (folder named `dos`) and drop your games in. !!! tip "Upload games as `.zip`" `dosbox-pure` knows how to unzip and auto-mount zipped DOS games. Much easier than packaging a raw folder. @@ -17,11 +17,11 @@ DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-p ## Game categories -DOS games come in three flavours — each needs a different approach: +DOS games come in three flavours, each needs a different approach: -- **Homebrew** — indie / modern DOS games. Just an `.exe`; usually Just Works once mounted. -- **Shareware demos** — what most "free DOS games" sites distribute. Same as homebrew — all files in one folder. -- **Retail** — need the original CD mounted alongside the installed game files. More work; every game is different. +- **Homebrew:** indie / modern DOS games. Just an `.exe`; usually Just Works once mounted. +- **Shareware demos:** what most "free DOS games" sites distribute. Same as homebrew: all files in one folder. +- **Retail:** need the original CD mounted alongside the installed game files. More work; every game is different. ## The manual way (commandline) @@ -34,7 +34,7 @@ dir # find the .EXE filename.exe # launch ``` -Works for homebrew and shareware. Retail games usually fail here — they want a CD mounted somewhere. +Works for homebrew and shareware. Retail games usually fail here; they want a CD mounted somewhere. ## The automatic way (`.conf` autoload) @@ -42,7 +42,7 @@ Works for homebrew and shareware. Retail games usually fail here — they want a Once you have a working `.conf`, click Play → game boots straight into DOS → `[autoexec]` runs → you're in the game. No typing. -### Homebrew / demo example — DOOM shareware +### Homebrew / demo example: DOOM shareware 1. Confirm no `.ins`, `.cue`, or `.bin` files in the folder (those mean it's a retail CD game). 2. Create `DOOM.conf` next to `DOOM.exe`: @@ -110,19 +110,19 @@ exit Walkthrough of `[autoexec]`: -- `Mount C ".."` — mount the current folder as drive `C:`. -- `C:` — switch to `C:`. -- `cls` — clear the screen. -- `DOOM.exe` — launch the game. -- `:exit` + `exit` — return after quitting. +- `Mount C ".."`: mount the current folder as drive `C:`. +- `C:`: switch to `C:`. +- `cls`: clear the screen. +- `DOOM.exe`: launch the game. +- `:exit` + `exit`: return after quitting. 3. Zip up the folder (including `DOOM.conf`). 4. Upload to RomM under the `dos` platform. 5. Click Play. Game boots. -Blank screen after boot? Something's off in `[autoexec]` — usually a path or mount issue. +Blank screen after boot? Something's off in `[autoexec]`, usually a path or mount issue. -### Retail with CD example — Dungeon Keeper +### Retail with CD example: Dungeon Keeper Retail games need the game CD image mounted alongside the install directory. @@ -136,7 +136,7 @@ Retail games need the game CD image mounted alongside the install directory. DUNGEON.CUE DUNGEON.BIN ``` -2. Read `KEEPER.cfg` — it tells you where the game expects the CD. +2. Read `KEEPER.cfg`: it tells you where the game expects the CD. 3. Create `KEEPER.conf` with this `[autoexec]`: ```ini @@ -154,12 +154,12 @@ Retail games need the game CD image mounted alongside the install directory. ``` Key lines: - - `Mount C ".."` — game files as `C:`. - - `imgmount d DUNGEO~8.CUE -t iso -fs iso` — CD image as `D:`. - - `cd ..` — back to `C:\` where `KEEPER.exe` lives. - - `KEEPER.exe` — run. + - `Mount C ".."`: game files as `C:`. + - `imgmount d DUNGEO~8.CUE -t iso -fs iso`: CD image as `D:`. + - `cd ..`: back to `C:\` where `KEEPER.exe` lives. + - `KEEPER.exe`: run. - (File names in DOS are 8.3 — `DUNGEON8.CUE` becomes `DUNGEO~8.CUE`.) + (File names in DOS are 8.3: `DUNGEON8.CUE` becomes `DUNGEO~8.CUE`.) 4. Zip, upload, play. @@ -170,9 +170,9 @@ Retail games need the game CD image mounted alongside the install directory. If the game lands on a blank DOS prompt: -1. **Remove the final `.exe` line from `[autoexec]`** — drops you at a prompt after mounting. Check manually: +1. **Remove the final `.exe` line from `[autoexec]`.** Drops you at a prompt after mounting. Check manually: - Is the game directory mounted correctly? - - `type DEFAULT.cfg` (or whatever the game's config is named) — the install path should line up with what you mounted. + - `type DEFAULT.cfg` (or whatever the game's config is named): the install path should line up with what you mounted. 2. **Check drive paths.** Many retail DOS games hard-code `D:\` as the CD. If you mount the CD as `E:`, the game won't find it. 3. **Debug with native `dosbox-pure`.** Run the same zip in [RetroArch](https://retroarch.com/) with the `dosbox-pure` core. If it works there, it should work in RomM. If it doesn't, the `.conf` is the problem. @@ -182,15 +182,15 @@ The DOOM example above is minimal. The full config can include dozens more optio ## Netplay on DOS -Not supported — `dosbox-pure` works with EmulatorJS Netplay intermittently, not reliably. Stick to single-player for DOS. +Not supported: `dosbox-pure` works with EmulatorJS Netplay intermittently, not reliably. Stick to single-player for DOS. ## Known issues -- **Mouse lag** — enable `autolock=true` in `[sdl]`. -- **Wrong resolution / aspect** — tweak `[render] aspect=` and `windowresolution=`. -- **Audio crackle** — lower `rate=22050` to `rate=11025` or raise the mixer blocksize. -- **Game runs too fast** — most auto-cycle issues; set `cycles=fixed 4000` (or another number) explicitly. -- **Keyboard doesn't work for certain keys** — `usescancodes=false` sometimes fixes it. +- **Mouse lag.** Enable `autolock=true` in `[sdl]`. +- **Wrong resolution / aspect.** Tweak `[render] aspect=` and `windowresolution=`. +- **Audio crackle.** Lower `rate=22050` to `rate=11025` or raise the mixer blocksize. +- **Game runs too fast.** Most auto-cycle issues; set `cycles=fixed 4000` (or another number) explicitly. +- **Keyboard doesn't work for certain keys.** `usescancodes=false` sometimes fixes it. ## Dosemu / Exodos alternatives @@ -198,6 +198,6 @@ Dosemu (Linux native) and Exodos (Windows frontend) are separate DOS tools, not ## See also -- [In-Browser Play](../using/in-browser-play.md) — general EmulatorJS behaviour. -- [EmulatorJS Configuration](emulatorjs-config.md) — operator-level tuning, including `disable_batch_bootup` for DOS-specific issues. -- [dosbox-pure docs](https://github.com/schellingb/dosbox-pure) — upstream reference for every config option. +- [In-Browser Play](../using/in-browser-play.md): general EmulatorJS behaviour. +- [EmulatorJS Configuration](emulatorjs-config.md): operator-level tuning, including `disable_batch_bootup` for DOS-specific issues. +- [dosbox-pure docs](https://github.com/schellingb/dosbox-pure): upstream reference for every config option. diff --git a/docs/platforms/ruffle-config.md b/docs/platforms/ruffle-config.md index 76abb22f..43409f6a 100644 --- a/docs/platforms/ruffle-config.md +++ b/docs/platforms/ruffle-config.md @@ -22,11 +22,11 @@ Ruffle **only** plays games from platform folders named `flash` or `browser`. No If your Flash games live under a differently-named folder: -### Option 1 — rename +### Option 1: rename Just rename the folder to `flash/` and rescan. -### Option 2 — remap via `config.yml` +### Option 2: remap via `config.yml` ```yaml system: @@ -38,17 +38,17 @@ Now `web-games/` is treated as the `flash` platform. See [Configuration File → ## Supported content -- **Flash (SWF)** — 2D games, most work cleanly. AS1/AS2 excellent; AS3 still maturing in Ruffle. -- **Shockwave (DCR)** — partial support; complex 3D Shockwave games often fail. -- **FLV / F4V** — Flash video; playable but not game-like. +- **Flash (SWF):** 2D games, most work cleanly. AS1/AS2 excellent; AS3 still maturing in Ruffle. +- **Shockwave (DCR):** partial support; complex 3D Shockwave games often fail. +- **FLV / F4V:** Flash video; playable but not game-like. [Ruffle compatibility database](https://ruffle.rs/#compatibility) has per-title status if you want to check a specific game. ## File naming -Ruffle honours the usual [RomM naming conventions](../getting-started/folder-structure.md#naming-convention) — filename tags, regions, revisions. +Ruffle honours the usual [RomM naming conventions](../getting-started/folder-structure.md#naming-convention): filename tags, regions, revisions. -No specific requirements beyond that — Ruffle reads the SWF directly. +No specific requirements beyond that; Ruffle reads the SWF directly. ## Saves @@ -63,37 +63,37 @@ environment: - FLASHPOINT_API_ENABLED=true ``` -Then run an **Unmatched** scan on your `flash` platform. Titles, descriptions, cover art, tags — all populated if the game exists in Flashpoint's ~180,000-entry database. +Then run an **Unmatched** scan on your `flash` platform. Titles, descriptions, cover art, tags: all populated if the game exists in Flashpoint's ~180,000-entry database. ## Controls Flash was designed around mouse + keyboard. Ruffle passes input through natively: -- **Mouse** — full support. -- **Keyboard** — full support. -- **Gamepad** — **not supported.** Flash games using XInput or similar don't work. +- **Mouse:** full support. +- **Keyboard:** full support. +- **Gamepad: not supported.** Flash games using XInput or similar don't work. On a handheld / Console Mode, Flash games are generally unplayable unless you've got a touchscreen + keyboard or a mouse. ## Version / updates -Ruffle is bundled in the full RomM image. Updates to Ruffle land when RomM updates its image — there's no separate Ruffle update knob. +Ruffle is bundled in the full RomM image. Updates to Ruffle land when RomM updates its image; there's no separate Ruffle update knob. ## Not in 5.0 yet -- **Per-game config overrides** — Ruffle supports some game-specific options upstream, but RomM doesn't surface them yet. -- **Networking** — Flash games that hit remote servers typically fail (those servers are dead). No proxy / emulated backend for networked Flash games. -- **Control remapping** — straight passthrough only. +- **Per-game config overrides.** Ruffle supports some game-specific options upstream, but RomM doesn't surface them yet. +- **Networking.** Flash games that hit remote servers typically fail (those servers are dead). No proxy / emulated backend for networked Flash games. +- **Control remapping.** Straight passthrough only. ## Troubleshooting -- **Play button is missing on a Flash game** — platform folder isn't named `flash` or `browser` (or mapped via `system.platforms`). -- **Game loads but blank** — AS3 game Ruffle doesn't handle yet. Check [Ruffle compatibility](https://ruffle.rs/#compatibility). -- **Game says "Ruffle not loaded"** — you're on the slim image. Switch to full, or set `ENABLE_RUFFLE=false` to hide the Play button cleanly. +- **Play button is missing on a Flash game.** Platform folder isn't named `flash` or `browser` (or mapped via `system.platforms`). +- **Game loads but blank.** AS3 game Ruffle doesn't handle yet. Check [Ruffle compatibility](https://ruffle.rs/#compatibility). +- **Game says "Ruffle not loaded".** You're on the slim image. Switch to full, or set `ENABLE_RUFFLE=false` to hide the Play button cleanly. See [In-Browser Play Troubleshooting → Ruffle](../troubleshooting/in-browser-play.md#ruffle-games). ## See also -- [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle) — end-user side. -- [Metadata Providers → Flashpoint](../administration/metadata-providers.md#flashpoint) — where Flash metadata comes from. +- [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle): end-user side. +- [Metadata Providers → Flashpoint](../administration/metadata-providers.md#flashpoint): where Flash metadata comes from. diff --git a/docs/platforms/supported-platforms.md b/docs/platforms/supported-platforms.md index 5bc0cc99..7951fd3f 100644 --- a/docs/platforms/supported-platforms.md +++ b/docs/platforms/supported-platforms.md @@ -11,7 +11,7 @@ RomM ships support for ~400 platforms. "Support" means: 2. **At least one metadata provider** has coverage. 3. **EmulatorJS** may have a playable core (flagged per platform in the table below). -Your folder name has to match the **platform slug** in the table. If yours differs, use [`system.platforms`](../reference/configuration-file.md#systemplatforms) in `config.yml` to remap — see [Folder Structure](../getting-started/folder-structure.md). +Your folder name has to match the **platform slug** in the table. If yours differs, use [`system.platforms`](../reference/configuration-file.md#systemplatforms) in `config.yml` to remap; see [Folder Structure](../getting-started/folder-structure.md). ## Platform slugs + coverage @@ -19,17 +19,17 @@ Your folder name has to match the **platform slug** in the table. If yours diffe ## What the columns mean -- **Slug** — the folder name RomM expects. Matches the IGDB platform slug where possible. -- **Name** — the human-readable platform name. -- **Providers** — which metadata providers have at least partial coverage. See [Metadata Providers](../administration/metadata-providers.md). -- **EmulatorJS** — a playable in-browser core exists. See [EmulatorJS Configuration](emulatorjs-config.md). -- **Firmware** — platform needs BIOS files for emulation. See [Firmware by Platform](firmware-by-platform.md). +- **Slug**: the folder name RomM expects. Matches the IGDB platform slug where possible. +- **Name**: the human-readable platform name. +- **Providers**: which metadata providers have at least partial coverage. See [Metadata Providers](../administration/metadata-providers.md). +- **EmulatorJS**: a playable in-browser core exists. See [EmulatorJS Configuration](emulatorjs-config.md). +- **Firmware**: platform needs BIOS files for emulation. See [Firmware by Platform](firmware-by-platform.md). ## Platform not listed? Two options: -- **[Custom Platforms](custom-platforms.md)** — add an unknown platform. RomM will recognise the folder but won't have metadata or emulator support. +- **[Custom Platforms](custom-platforms.md)**: add an unknown platform. RomM will recognise the folder but won't have metadata or emulator support. - **Map to an existing slug** via `config.yml` if yours is a naming variant of something RomM already supports (e.g. `super_nintendo` → `snes`). Open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) if you believe a platform should be added to the built-in list. @@ -44,8 +44,8 @@ uv run python docs/scripts/gen_platforms.py ## See also -- [Folder Structure](../getting-started/folder-structure.md) — how platform slugs map to on-disk folders. -- [Custom Platforms](custom-platforms.md) — adding platforms outside the built-in list. -- [Metadata Providers](../administration/metadata-providers.md) — provider coverage deep-dive. -- [In-Browser Play](../using/in-browser-play.md) — EmulatorJS core catalogue. -- [Firmware by Platform](firmware-by-platform.md) — per-platform BIOS requirements. +- [Folder Structure](../getting-started/folder-structure.md): how platform slugs map to on-disk folders. +- [Custom Platforms](custom-platforms.md): adding platforms outside the built-in list. +- [Metadata Providers](../administration/metadata-providers.md): provider coverage deep-dive. +- [In-Browser Play](../using/in-browser-play.md): EmulatorJS core catalogue. +- [Firmware by Platform](firmware-by-platform.md): per-platform BIOS requirements. diff --git a/docs/reference/configuration-file.md b/docs/reference/configuration-file.md index b3bd2206..851d1004 100644 --- a/docs/reference/configuration-file.md +++ b/docs/reference/configuration-file.md @@ -1,11 +1,11 @@ --- title: Configuration File -description: Full schema reference for config.yml — exclusions, system bindings, filesystem, scan priorities, EmulatorJS. +description: Full schema reference for config.yml: exclusions, system bindings, filesystem, scan priorities, EmulatorJS. --- # Configuration File -RomM reads `config.yml` from `/romm/config/config.yml` inside the container. The whole file is optional — any section you omit falls back to RomM's defaults. +RomM reads `config.yml` from `/romm/config/config.yml` inside the container. The whole file is optional: any section you omit falls back to RomM's defaults. You can edit `config.yml` directly on disk **or** through **Administration → Library Management** in the UI, which is a two-way view of the same file. @@ -15,7 +15,7 @@ Start from the [`config.example.yml`](https://github.com/rommapp/romm/blob/maste - [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) !!! warning "Only set what you need" - Any omitted section uses the default. Don't copy the full example and then strip sections — just add what you want to change. + Any omitted section uses the default. Don't copy the full example and then strip sections; just add what you want to change. --- @@ -25,7 +25,7 @@ Control what the scanner ignores. ### `exclude.platforms` -Skip entire platform folders. Values are platform slugs (not folder names — see [`system.platforms`](#systemplatforms) if your folders are named differently). +Skip entire platform folders. Values are platform slugs (not folder names; see [`system.platforms`](#systemplatforms) if your folders are named differently). ```yaml exclude: @@ -256,7 +256,7 @@ Which media types to fetch during a scan. Applies primarily to ScreenScraper and | `fanart` | Community-uploaded fan art. | | `bezel` | EmulatorJS-compatible bezel. | | `manual` | PDF manual. Enabled by default. | -| `video` | Gameplay video (big files — watch your disk). | +| `video` | Gameplay video (big files, watch your disk). | ```yaml scan: @@ -372,7 +372,7 @@ emulatorjs: fps: show ``` -Core names must match the EmulatorJS core identifier exactly — see the `getSupportedEJSCores` utility in the frontend source for the full list, or leave the core out and use `default`. +Core names must match the EmulatorJS core identifier exactly. See the `getSupportedEJSCores` utility in the frontend source for the full list, or leave the core out and use `default`. ### `emulatorjs.controls` @@ -398,18 +398,18 @@ See the [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/contr ## Editing via the UI -Everything above is also available from the Library Management page in the web UI — edits there write back to the same `config.yml`. Either path works; they're not separate stores. +Everything above is also available from the Library Management page in the web UI; edits there write back to the same `config.yml`. Either path works; they're not separate stores. ## Per-file alternatives -RomM also ships two pre-built config.yml variants for people coming from existing frontends — copy them wholesale rather than writing one from scratch: +RomM also ships two pre-built config.yml variants for people coming from existing frontends. Copy them wholesale rather than writing one from scratch: -- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml) — Batocera / RetroBat layouts. -- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) — ES-DE layout. +- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml): Batocera / RetroBat layouts. +- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml): ES-DE layout. ## Related -- [Folder Structure](../getting-started/folder-structure.md) — how the filesystem shape interacts with `config.yml`. -- [Metadata Providers](../administration/metadata-providers.md) — per-provider detail for the `scan.priority.*` slugs. -- [Scanning & Watcher](../administration/scanning-and-watcher.md) — how `exclude.*` interacts with scan runs. -- [Environment Variables](environment-variables.md) — env-var overrides for some of the same knobs. +- [Folder Structure](../getting-started/folder-structure.md): how the filesystem shape interacts with `config.yml`. +- [Metadata Providers](../administration/metadata-providers.md): per-provider detail for the `scan.priority.*` slugs. +- [Scanning & Watcher](../administration/scanning-and-watcher.md): how `exclude.*` interacts with scan runs. +- [Environment Variables](environment-variables.md): env-var overrides for some of the same knobs. diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 4699dbd4..5bc991da 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -7,7 +7,7 @@ description: Every environment variable RomM reads, grouped by what it controls. Everything RomM does that's not in [`config.yml`](configuration-file.md) is driven by env vars. Set them on the `romm` service in your compose file, as Unraid / Synology / TrueNAS container env vars, or on your Kubernetes deployment. -This page is the **authoritative lookup** — every var RomM reads. The table is generated directly from [`rommapp/romm`'s `env.template`][src] at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). When RomM adds an env var, the next docs bump re-runs the generator and this page updates. +This page is the **authoritative lookup**: every var RomM reads. The table is generated directly from [`rommapp/romm`'s `env.template`][src] at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). When RomM adds an env var, the next docs bump re-runs the generator and this page updates. [src]: https://github.com/rommapp/romm/blob/master/env.template @@ -47,7 +47,7 @@ You'll always set these: | Variable | Purpose | | --- | --- | -| `ROMM_AUTH_SECRET_KEY` | JWT signing key. Generate with `openssl rand -hex 32`. **Never rotate lightly** — breaks all sessions. | +| `ROMM_AUTH_SECRET_KEY` | JWT signing key. Generate with `openssl rand -hex 32`. **Never rotate lightly**: breaks all sessions. | | `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | | `ROMM_DB_DRIVER` | `mariadb` (default), `mysql`, `postgresql`, or `sqlite`. See [Databases](../install/databases.md). | @@ -56,7 +56,7 @@ For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../a ## When env vars are read - **Startup.** Most vars are consumed once at container start. Change requires `docker compose up -d` to apply. -- **Per-request.** A handful of feature toggles (`KIOSK_MODE`, `DISABLE_USERPASS_LOGIN`) are re-checked per request — still, restart is safest to avoid partial-state caching. +- **Per-request.** A handful of feature toggles (`KIOSK_MODE`, `DISABLE_USERPASS_LOGIN`) are re-checked per request. Still, restart is safest to avoid partial-state caching. - **Never at runtime.** There's no reload-config endpoint. ## Full reference @@ -65,7 +65,7 @@ For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../a ## See also -- [Configuration File](configuration-file.md) — everything that lives in `config.yml` rather than env vars. -- [Scheduled Tasks](scheduled-tasks.md) — cron-controlling env vars in context. -- [Authentication](../administration/authentication.md) — auth-related env vars in narrative form. -- [Metadata Providers](../administration/metadata-providers.md) — per-provider credential env vars. +- [Configuration File](configuration-file.md): everything that lives in `config.yml` rather than env vars. +- [Scheduled Tasks](scheduled-tasks.md): cron-controlling env vars in context. +- [Authentication](../administration/authentication.md): auth-related env vars in narrative form. +- [Metadata Providers](../administration/metadata-providers.md): per-provider credential env vars. diff --git a/docs/reference/exports.md b/docs/reference/exports.md index 15c110ea..1b54e937 100644 --- a/docs/reference/exports.md +++ b/docs/reference/exports.md @@ -1,6 +1,6 @@ --- title: Exports -description: Export RomM metadata for use in other frontends — gamelist.xml and Pegasus format. +description: Export RomM metadata for use in other frontends: gamelist.xml and Pegasus format. --- # Exports @@ -70,8 +70,8 @@ Once RomM has generated `gamelist.xml` and populated `covers/` + `screenshots/`, ``` -- `MediaDirectory` — point it at the ROM folder (same path ES-DE uses for `ROMDirectory`), so ES-DE looks for media in-place rather than in its own library. -- `LegacyGamelistFileLocation` — makes ES-DE write updates back to the same `gamelist.xml` RomM reads from, rather than its separate config dir. +- `MediaDirectory`: point it at the ROM folder (same path ES-DE uses for `ROMDirectory`), so ES-DE looks for media in-place rather than in its own library. +- `LegacyGamelistFileLocation`: makes ES-DE write updates back to the same `gamelist.xml` RomM reads from, rather than its separate config dir. See also [Metadata Providers → gamelist.xml](../administration/metadata-providers.md) for the *import* direction (reading gamelist.xml into RomM). @@ -127,7 +127,7 @@ Not in 5.0. If you want another format (LaunchBox, Attract-Mode, etc.), open an Exports don't auto-rerun on every metadata edit. Triggers: -- **Next scan** — exports are part of scan completion when enabled. +- **Next scan**: exports are part of scan completion when enabled. - **Manual trigger** via the API above. - **Admin → Tasks → Export** (if surfaced in your build). @@ -135,14 +135,14 @@ For tight sync between RomM edits and the external frontend, run the export via ## Hash stability -Both export formats include some IDs (`igdb_id`, `moby_id`, etc.). If you rematch metadata, those IDs change. Don't point tools at the IDs as stable keys — use filenames / paths. +Both export formats include some IDs (`igdb_id`, `moby_id`, etc.). If you rematch metadata, those IDs change. Don't point tools at the IDs as stable keys; use filenames / paths. ## Permissions -`tasks.run` scope required to trigger an export via API. Config-driven export runs as the scan does — no extra permission check. +`tasks.run` scope required to trigger an export via API. Config-driven export runs as the scan does, with no extra permission check. ## See also - [Configuration File → `scan.gamelist`](../reference/configuration-file.md#scangamelistexport-new-in-50) - [Configuration File → `scan.pegasus`](../reference/configuration-file.md#scanpegasusexport-new-in-50) -- [Metadata Providers → gamelist.xml importer](../administration/metadata-providers.md) — reverse direction. +- [Metadata Providers → gamelist.xml importer](../administration/metadata-providers.md): reverse direction. diff --git a/docs/reference/feeds.md b/docs/reference/feeds.md index c34c70b5..c0b5cd89 100644 --- a/docs/reference/feeds.md +++ b/docs/reference/feeds.md @@ -35,8 +35,8 @@ Plus legacy `pkgj` formats for individual platforms: All feeds respect `DISABLE_DOWNLOAD_ENDPOINT_AUTH`: -- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=false`** (default, secure) — feeds require basic auth. Most clients (Tinfoil, pkgj, fpkgi) can send basic auth in their URL config. -- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`** — feeds are public. Use when RomM is behind upstream auth (reverse proxy with auth, VPN). +- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=false`** (default, secure): feeds require basic auth. Most clients (Tinfoil, pkgj, fpkgi) can send basic auth in their URL config. +- **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`**: feeds are public. Use when RomM is behind upstream auth (reverse proxy with auth, VPN). See [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass) for the full security discussion. @@ -63,7 +63,7 @@ Most feeds return JSON: Tinfoil, fpkgi, WebRcade. ### CSV feeds -pkgi uses CSV per upstream's format — one line per game: +pkgi uses CSV per upstream's format: one line per game: ```csv psvita,PCSA00003,Unknown,Game,1.0,https://romm.example.com/...,md5=...,52428800 @@ -71,7 +71,7 @@ psvita,PCSA00003,Unknown,Game,1.0,https://romm.example.com/...,md5=...,52428800 ### Format per feed -Each feed's exact schema matches what its client expects. Don't call feeds by accident from other tools — Tinfoil can't parse WebRcade JSON, and vice versa. +Each feed's exact schema matches what its client expects. Don't call feeds by accident from other tools. Tinfoil can't parse WebRcade JSON, and vice versa. ## Filtering @@ -86,7 +86,7 @@ Games not matching the expected extension are silently skipped, not reported as ## Performance notes -Feeds query the whole library for the target platform on every request. For large libraries that's measurable but not slow (< 1s typically). Clients generally cache feed responses — don't hit the feed in a tight loop. +Feeds query the whole library for the target platform on every request. For large libraries that's measurable but not slow (< 1s typically). Clients generally cache feed responses, so don't hit the feed in a tight loop. ## Platform slugs @@ -100,10 +100,10 @@ If you want RomM to expose a feed format it doesn't currently support: 1. Check the [backend/routers/feeds/](https://github.com/rommapp/romm/tree/master/backend/routers) directory for existing implementations as templates. 2. Open an issue describing the target client, its feed format, and any authentication requirements. -3. Ideally, open a PR. Feed endpoints are usually small — a few dozen lines of Python to filter + format. +3. Ideally, open a PR. Feed endpoints are usually small: a few dozen lines of Python to filter + format. ## See also -- [Ecosystem](../ecosystem/index.md) — per-client setup guides. -- [Authentication](../administration/authentication.md) — auth options affecting feeds. -- [API Reference](../developers/api-reference.md) — full endpoint catalogue. +- [Ecosystem](../ecosystem/index.md): per-client setup guides. +- [Authentication](../administration/authentication.md): auth options affecting feeds. +- [API Reference](../developers/api-reference.md): full endpoint catalogue. diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md index bdeb7954..7dea77ca 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -11,131 +11,131 @@ For the pedagogical version with reasoning and examples, see [Core Concepts](../ --- -**Admin** — highest user role. Full scope set. User management and task execution. See [Users & Roles](../administration/users-and-roles.md). +**Admin**: highest user role. Full scope set. User management and task execution. See [Users & Roles](../administration/users-and-roles.md). -**API token** — see Client API Token. +**API token**: see Client API Token. -**Argosy** — RomM's first-party Android launcher app. See [Argosy](../ecosystem/argosy.md). +**Argosy**: RomM's first-party Android launcher app. See [Argosy](../ecosystem/argosy.md). -**Asset** — user-uploaded content attached to a ROM: save files, emulator states, screenshots. Stored under `/romm/assets/`. Per-user. [Saves & States](../using/saves-and-states.md). +**Asset**: user-uploaded content attached to a ROM: save files, emulator states, screenshots. Stored under `/romm/assets/`. Per-user. [Saves & States](../using/saves-and-states.md). -**Autologin** — OIDC feature that bypasses RomM's login page and redirects straight to the IdP. Set via `OIDC_AUTOLOGIN=true`. +**Autologin**: OIDC feature that bypasses RomM's login page and redirects straight to the IdP. Set via `OIDC_AUTOLOGIN=true`. -**Basic auth** — HTTP auth with username+password in a header. Supported for API calls. +**Basic auth**: HTTP auth with username+password in a header. Supported for API calls. -**BIOS** — see Firmware. +**BIOS**: see Firmware. -**Client API Token** — long-lived bearer token scoped to a user. Used by companion apps. 25/user max. [Client API Tokens](../ecosystem/client-api-tokens.md). +**Client API Token**: long-lived bearer token scoped to a user. Used by companion apps. 25/user max. [Client API Tokens](../ecosystem/client-api-tokens.md). -**Collection** — a named group of ROMs. Three flavours: standard (hand-curated), smart (rule-based), virtual (auto-generated by RomM). [Collections](../using/collections.md). +**Collection**: a named group of ROMs. Three flavours: standard (hand-curated), smart (rule-based), virtual (auto-generated by RomM). [Collections](../using/collections.md). -**Console Mode** — separate `/console` UI optimised for TVs and gamepads. [Console Mode](../using/console-mode.md). +**Console Mode**: separate `/console` UI optimised for TVs and gamepads. [Console Mode](../using/console-mode.md). -**Device** — a registered endpoint that syncs with RomM. Tracked via [Device Sync Protocol](../ecosystem/device-sync-protocol.md). +**Device**: a registered endpoint that syncs with RomM. Tracked via [Device Sync Protocol](../ecosystem/device-sync-protocol.md). -**Editor** — mid-tier user role. Edit content (ROMs, platforms, collections), upload; no user management. See [Users & Roles](../administration/users-and-roles.md). +**Editor**: mid-tier user role. Edit content (ROMs, platforms, collections), upload; no user management. See [Users & Roles](../administration/users-and-roles.md). -**EmulatorJS** — the in-browser retro emulator RomM uses for most platforms. [In-Browser Play](../using/in-browser-play.md). +**EmulatorJS**: the in-browser retro emulator RomM uses for most platforms. [In-Browser Play](../using/in-browser-play.md). -**Feed** — a URL endpoint that exposes a filtered library view in a third-party tool's expected format. [Feeds reference](feeds.md). +**Feed**: a URL endpoint that exposes a filtered library view in a third-party tool's expected format. [Feeds reference](feeds.md). -**Firmware** — BIOS files some emulators need. Lives under `/romm/library/bios/` (or per-platform bios folders). [Firmware Management](../administration/firmware-management.md). +**Firmware**: BIOS files some emulators need. Lives under `/romm/library/bios/` (or per-platform bios folders). [Firmware Management](../administration/firmware-management.md). -**Full image** — the default RomM container variant, including EmulatorJS + Ruffle. `rommapp/romm:X.Y.Z`. See [Image Variants](../install/image-variants.md). +**Full image**: the default RomM container variant, including EmulatorJS + Ruffle. `rommapp/romm:X.Y.Z`. See [Image Variants](../install/image-variants.md). -**Game Data tab** — the ROM detail page tab for saves, states, and screenshots. User-specific. +**Game Data tab**: the ROM detail page tab for saves, states, and screenshots. User-specific. -**gamelist.xml** — ES-DE / Batocera-compatible metadata format. RomM can both import (as a metadata source) and export. +**gamelist.xml**: ES-DE / Batocera-compatible metadata format. RomM can both import (as a metadata source) and export. -**Grout** — RomM's first-party Linux handheld companion (muOS, NextUI). [Grout](../ecosystem/grout.md). +**Grout**: RomM's first-party Linux handheld companion (muOS, NextUI). [Grout](../ecosystem/grout.md). -**Hasheous** — metadata provider doing hash-based matching (no API keys). [Metadata Providers → Hasheous](../administration/metadata-providers.md#hasheous). +**Hasheous**: metadata provider doing hash-based matching (no API keys). [Metadata Providers → Hasheous](../administration/metadata-providers.md#hasheous). -**IGDB** — Internet Game Database. Primary metadata provider. [Metadata Providers → IGDB](../administration/metadata-providers.md#igdb). +**IGDB**: Internet Game Database. Primary metadata provider. [Metadata Providers → IGDB](../administration/metadata-providers.md#igdb). -**Igir** — third-party ROM collection manager. Useful for cleaning libraries before importing into RomM. [Igir](../ecosystem/igir.md). +**Igir**: third-party ROM collection manager. Useful for cleaning libraries before importing into RomM. [Igir](../ecosystem/igir.md). -**Invite link** — single-use URL that lets a new user register with a pre-assigned role. [Invitations & Registration](../administration/invitations-and-registration.md). +**Invite link**: single-use URL that lets a new user register with a pre-assigned role. [Invitations & Registration](../administration/invitations-and-registration.md). -**Kekatsu** — Nintendo DS multiboot loader that reads RomM's feed. [Kekatsu](../ecosystem/kekatsu.md). +**Kekatsu**: Nintendo DS multiboot loader that reads RomM's feed. [Kekatsu](../ecosystem/kekatsu.md). -**Kiosk mode** — `KIOSK_MODE=true` setting that turns every read endpoint into unauthenticated access. [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). +**Kiosk mode**: `KIOSK_MODE=true` setting that turns every read endpoint into unauthenticated access. [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). -**Library** — your ROM files on disk. Mounted as `/romm/library`. Platforms are subdirectories. [Folder Structure](../getting-started/folder-structure.md). +**Library**: your ROM files on disk. Mounted as `/romm/library`. Platforms are subdirectories. [Folder Structure](../getting-started/folder-structure.md). -**LaunchBox** — metadata provider; uses a local downloaded DB. +**LaunchBox**: metadata provider; uses a local downloaded DB. -**Metadata provider** — external source of game data (IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, PlayMatch, LaunchBox, SteamGridDB, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro — 13 total in 5.0). [Metadata Providers](../administration/metadata-providers.md). +**Metadata provider**: external source of game data (IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, PlayMatch, LaunchBox, SteamGridDB, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro: 13 total in 5.0). [Metadata Providers](../administration/metadata-providers.md). -**mike** — versioning tool for MkDocs used by the RomM docs site. +**mike**: versioning tool for MkDocs used by the RomM docs site. -**MobyGames** — paid metadata provider. [Metadata Providers → MobyGames](../administration/metadata-providers.md#mobygames). +**MobyGames**: paid metadata provider. [Metadata Providers → MobyGames](../administration/metadata-providers.md#mobygames). -**muOS** — custom firmware for ARM handhelds. RomM has a muOS-specific app. [muOS App](../ecosystem/muos-app.md). +**muOS**: custom firmware for ARM handhelds. RomM has a muOS-specific app. [muOS App](../ecosystem/muos-app.md). -**Netplay** — multi-player EmulatorJS mode over WebRTC. [Netplay](../using/netplay.md). +**Netplay**: multi-player EmulatorJS mode over WebRTC. [Netplay](../using/netplay.md). -**OIDC** — OpenID Connect. Standard SSO protocol RomM supports for external auth. [OIDC Setup](../administration/oidc/index.md). +**OIDC**: OpenID Connect. Standard SSO protocol RomM supports for external auth. [OIDC Setup](../administration/oidc/index.md). -**OpenTelemetry (OTEL)** — opt-in traces/metrics/logs export. `OTEL_ENABLED=true`. [Observability](../administration/observability.md). +**OpenTelemetry (OTEL)**: opt-in traces/metrics/logs export. `OTEL_ENABLED=true`. [Observability](../administration/observability.md). -**Personal tab** — the ROM detail page tab for per-user data (rating, status, notes, playtime). +**Personal tab**: the ROM detail page tab for per-user data (rating, status, notes, playtime). -**pkgj** — PS Vita / PSP homebrew installer. Consumes RomM feeds. [pkgj](../ecosystem/pkgj.md). +**pkgj**: PS Vita / PSP homebrew installer. Consumes RomM feeds. [pkgj](../ecosystem/pkgj.md). -**Platform** — a gaming system (SNES, PSX, GBA, etc.). Each has a canonical slug. [Supported Platforms](../platforms/supported-platforms.md). +**Platform**: a gaming system (SNES, PSX, GBA, etc.). Each has a canonical slug. [Supported Platforms](../platforms/supported-platforms.md). -**Play session** — timestamped record of someone playing a ROM. +**Play session**: timestamped record of someone playing a ROM. -**Playnite Plugin** — RomM's Windows Playnite integration. [Playnite Plugin](../ecosystem/playnite-plugin.md). +**Playnite Plugin**: RomM's Windows Playnite integration. [Playnite Plugin](../ecosystem/playnite-plugin.md). -**PWA** — Progressive Web App. Install RomM to your home screen. [Install as PWA](../using/pwa.md). +**PWA**: Progressive Web App. Install RomM to your home screen. [Install as PWA](../using/pwa.md). -**ROM** — a single game entry in RomM. Can be a single file or a multi-file folder. Belongs to exactly one platform. +**ROM**: a single game entry in RomM. Can be a single file or a multi-file folder. Belongs to exactly one platform. -**RetroAchievements (RA)** — achievements service RomM integrates with. Per-user linking. [RetroAchievements](../using/retroachievements.md). +**RetroAchievements (RA)**: achievements service RomM integrates with. Per-user linking. [RetroAchievements](../using/retroachievements.md). -**Resource** — provider-fetched metadata image (cover, screenshot, manual). Stored under `/romm/resources/`. Rebuildable from a rescan. +**Resource**: provider-fetched metadata image (cover, screenshot, manual). Stored under `/romm/resources/`. Rebuildable from a rescan. -**RomM** — this project. Pronounced "rom-em" (rhymes with "problem"). +**RomM**: this project. Pronounced "rom-em" (rhymes with "problem"). -**RQ** — Redis Queue, the task-queue library RomM uses for background work. +**RQ**: Redis Queue, the task-queue library RomM uses for background work. -**Ruffle** — the in-browser Flash / Shockwave emulator RomM uses. [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle). +**Ruffle**: the in-browser Flash / Shockwave emulator RomM uses. [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle). -**Scan** — walk the library, hash files, match metadata, update the DB. Six modes. [Scanning & Watcher](../administration/scanning-and-watcher.md). +**Scan**: walk the library, hash files, match metadata, update the DB. Six modes. [Scanning & Watcher](../administration/scanning-and-watcher.md). -**Scope** — fine-grained permission. 19 in total, grouped into roles. [scope matrix](../administration/users-and-roles.md#scope-matrix). +**Scope**: fine-grained permission. 19 in total, grouped into roles. [scope matrix](../administration/users-and-roles.md#scope-matrix). -**ScreenScraper** — metadata provider with good artwork. [Metadata Providers → ScreenScraper](../administration/metadata-providers.md#screenscraper). +**ScreenScraper**: metadata provider with good artwork. [Metadata Providers → ScreenScraper](../administration/metadata-providers.md#screenscraper). -**Setup Wizard** — first-run flow that creates the admin user. Shown before any user exists. +**Setup Wizard**: first-run flow that creates the admin user. Shown before any user exists. -**Slim image** — smaller container variant without EmulatorJS or Ruffle. `rommapp/romm:X.Y.Z-slim`. See [Image Variants](../install/image-variants.md). +**Slim image**: smaller container variant without EmulatorJS or Ruffle. `rommapp/romm:X.Y.Z-slim`. See [Image Variants](../install/image-variants.md). -**Smart Collection** — rule-based auto-populating collection. [Smart Collections](../using/smart-collections.md). +**Smart Collection**: rule-based auto-populating collection. [Smart Collections](../using/smart-collections.md). -**Socket.IO** — RomM's WebSocket protocol. Two endpoints: `/ws` and `/netplay`. [WebSockets](../developers/websockets.md). +**Socket.IO**: RomM's WebSocket protocol. Two endpoints: `/ws` and `/netplay`. [WebSockets](../developers/websockets.md). -**SteamGridDB** — alternate cover art provider. [Metadata Providers → SteamGridDB](../administration/metadata-providers.md#steamgriddb). +**SteamGridDB**: alternate cover art provider. [Metadata Providers → SteamGridDB](../administration/metadata-providers.md#steamgriddb). -**Task** — a unit of background work in RomM. Runs via RQ. Scheduled / manual / watcher. [Scheduled Tasks](../administration/scheduled-tasks.md). +**Task**: a unit of background work in RomM. Runs via RQ. Scheduled / manual / watcher. [Scheduled Tasks](../administration/scheduled-tasks.md). -**TheGamesDB (TGDB)** — free community metadata provider. New in 5.0. +**TheGamesDB (TGDB)**: free community metadata provider. New in 5.0. -**Tinfoil** — Nintendo Switch homebrew that installs from RomM's feed. [Tinfoil](../ecosystem/tinfoil.md). +**Tinfoil**: Nintendo Switch homebrew that installs from RomM's feed. [Tinfoil](../ecosystem/tinfoil.md). -**User** — an account. One of three roles (Viewer / Editor / Admin). +**User**: an account. One of three roles (Viewer / Editor / Admin). -**Valkey** — open-source Redis fork; drop-in compatible. See [Redis or Valkey](../install/redis-or-valkey.md). +**Valkey**: open-source Redis fork; drop-in compatible. See [Redis or Valkey](../install/redis-or-valkey.md). -**Viewer** — lowest user role. Read-only on library, own saves/states/profile. +**Viewer**: lowest user role. Read-only on library, own saves/states/profile. -**Virtual Collection** — auto-generated collection by genre / developer / year / tag. Read-only. [Virtual Collections](../using/virtual-collections.md). +**Virtual Collection**: auto-generated collection by genre / developer / year / tag. Read-only. [Virtual Collections](../using/virtual-collections.md). -**Watcher** — filesystem watcher that triggers scans on file events. `WATCHER_ENABLED=true`. [Scanning & Watcher](../administration/scanning-and-watcher.md#filesystem-watcher). +**Watcher**: filesystem watcher that triggers scans on file events. `WATCHER_ENABLED=true`. [Scanning & Watcher](../administration/scanning-and-watcher.md#filesystem-watcher). -**WebRcade** — alternative browser frontend that consumes RomM's feed. [WebRcade](../ecosystem/webrcade.md). +**WebRcade**: alternative browser frontend that consumes RomM's feed. [WebRcade](../ecosystem/webrcade.md). --- diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index c673e0ec..0dd285e0 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -11,8 +11,8 @@ Quick reference for the ports + URL paths a RomM instance exposes. | Port | Protocol | Purpose | Exposed? | | --- | --- | --- | --- | -| `8080` | HTTP | nginx — the front door. Serves everything. | Yes — publish this. | -| `5000` | HTTP | gunicorn — FastAPI backend. Internal only. | No — nginx proxies here. | +| `8080` | HTTP | nginx: the front door. Serves everything. | Yes, publish this. | +| `5000` | HTTP | gunicorn: FastAPI backend. Internal only. | No; nginx proxies here. | | `6379` | TCP | Redis. Internal only (unless you externalise). | No. | Only **`8080`** should be reachable from outside the container in production. Typical compose `ports:`: @@ -44,7 +44,7 @@ Everything below is served by nginx on port 8080. Auth / protection depends on t ### Session-authenticated (cookies) -Most of the RomM web UI — every page under `/`. Authenticated via browser cookie. +Most of the RomM web UI: every page under `/`. Authenticated via browser cookie. ### Token-authenticated (bearer) @@ -128,6 +128,6 @@ Not the most common pattern; most deployments use a subdomain (`romm.example.com ## See also -- [Reverse Proxy](../install/reverse-proxy.md) — passthrough recipes per proxy. -- [Environment Variables](environment-variables.md) — including `ROMM_PORT`, `ROMM_BASE_PATH`, `ROMM_BASE_URL`. -- [Architecture](../developers/architecture.md) — how the port layout fits into the bigger picture. +- [Reverse Proxy](../install/reverse-proxy.md): passthrough recipes per proxy. +- [Environment Variables](environment-variables.md): including `ROMM_PORT`, `ROMM_BASE_PATH`, `ROMM_BASE_URL`. +- [Architecture](../developers/architecture.md): how the port layout fits into the bigger picture. diff --git a/docs/reference/scheduled-tasks.md b/docs/reference/scheduled-tasks.md index 877f4819..7a3295a0 100644 --- a/docs/reference/scheduled-tasks.md +++ b/docs/reference/scheduled-tasks.md @@ -7,11 +7,11 @@ description: Every background task RomM runs, with default schedule, env var, an RomM runs background work via **RQ** (Redis Queue). Tasks fall into three categories: -- **Scheduled** — cron-driven, fire on their own. -- **Watcher** — triggered by filesystem events. -- **Manual** — admin-triggered from the UI or API. +- **Scheduled**: cron-driven, fire on their own. +- **Watcher**: triggered by filesystem events. +- **Manual**: admin-triggered from the UI or API. -This page is the **lookup reference** — every task, its cron default, its env var, and a one-line purpose. For the narrative (how to tune cadence, how to trigger manually, how to monitor), see [Administration → Scheduled Tasks](../administration/scheduled-tasks.md). +This page is the **lookup reference**: every task, its cron default, its env var, and a one-line purpose. For the narrative (how to tune cadence, how to trigger manually, how to monitor), see [Administration → Scheduled Tasks](../administration/scheduled-tasks.md). ## Full task table @@ -27,10 +27,10 @@ minute hour day-of-month month day-of-week Examples: -- `0 3 * * *` — 3 AM daily. -- `0 */6 * * *` — every 6 hours. -- `*/30 * * * *` — every 30 minutes. -- `0 2 * * 0` — 2 AM every Sunday. +- `0 3 * * *`: 3 AM daily. +- `0 */6 * * *`: every 6 hours. +- `*/30 * * * *`: every 30 minutes. +- `0 2 * * 0`: 2 AM every Sunday. Set the matching env var from the table above, restart the container, and the scheduler picks up the new cadence on startup. @@ -38,7 +38,7 @@ Set the matching env var from the table above, restart the container, and the sc Two approaches: -- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`) — cleanest. +- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`); cleanest. - **Cron-it-off.** Set the cron to a moment that effectively never fires. `0 0 31 2 *` (Feb 31st) works. ## Where the table comes from @@ -47,7 +47,7 @@ Generated by `docs/scripts/gen_scheduled_tasks.py` against the task registry at ## See also -- [Administration → Scheduled Tasks](../administration/scheduled-tasks.md) — full narrative, per-task purpose, monitoring, tuning. -- [Environment Variables](environment-variables.md) — all task-related env vars in context. -- [Scanning & Watcher](../administration/scanning-and-watcher.md) — the scan and watcher tasks in depth. -- [SSH Sync](../administration/ssh-sync.md) — the push-pull sync task in depth. +- [Administration → Scheduled Tasks](../administration/scheduled-tasks.md): full narrative, per-task purpose, monitoring, tuning. +- [Environment Variables](environment-variables.md): all task-related env vars in context. +- [Scanning & Watcher](../administration/scanning-and-watcher.md): the scan and watcher tasks in depth. +- [SSH Sync](../administration/ssh-sync.md): the push-pull sync task in depth. diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md index 7a4f93cc..eb92a240 100644 --- a/docs/releases/changelog.md +++ b/docs/releases/changelog.md @@ -5,7 +5,7 @@ description: Summary of notable RomM releases; authoritative per-release notes l # Changelog -This page summarises major RomM releases. **The authoritative per-release changelog lives on [GitHub Releases](https://github.com/rommapp/romm/releases)** — every tag there has full commit lists, breaking changes flagged, and upgrade notes. +This page summarises major RomM releases. **The authoritative per-release changelog lives on [GitHub Releases](https://github.com/rommapp/romm/releases)**; every tag there has full commit lists, breaking changes flagged, and upgrade notes. For migration-grade detail on a major version, go straight to that version's guide: @@ -18,13 +18,13 @@ For migration-grade detail on a major version, go straight to that version's gui The biggest release since the project started. Highlights: -- **Console Mode** — a gamepad-first `/console` UI for TVs. -- **Smart & Virtual Collections** — rule-based and auto-generated collections. -- **ROM Patcher** — apply IPS/UPS/BPS/PPF and more in-browser. -- **Netplay** — EmulatorJS multiplayer with ICE servers. +- **Console Mode:** a gamepad-first `/console` UI for TVs. +- **Smart & Virtual Collections:** rule-based and auto-generated collections. +- **ROM Patcher:** apply IPS/UPS/BPS/PPF and more in-browser. +- **Netplay:** EmulatorJS multiplayer with ICE servers. - **Device sync protocol** + Client API Tokens with device pairing. - **OIDC role mapping** via claims (`OIDC_CLAIM_ROLES`). -- **Thirteen metadata providers** — added TheGamesDB, Libretro, gamelist.xml importer. +- **Thirteen metadata providers:** added TheGamesDB, Libretro, gamelist.xml importer. - **PWA install** support. - **19 locales**. - **OpenTelemetry** export via `OTEL_ENABLED`. @@ -34,8 +34,8 @@ The biggest release since the project started. Highlights: Highlights rolled up: -- **4.8** — stability and per-platform stats opt-in. -- **4.x** — iterative improvements on the 3.x → 4.x baseline: expanded metadata sources, frontend polish, first-class companion-app support. +- **4.8:** stability and per-platform stats opt-in. +- **4.x:** iterative improvements on the 3.x → 4.x baseline: expanded metadata sources, frontend polish, first-class companion-app support. The 4.x docs live at [docs.romm.app/4.8/](https://docs.romm.app/4.8/) as a frozen snapshot. See [Release Notes & Migration → Docs versions](index.md#docs-versions). @@ -45,10 +45,10 @@ The 4.x docs live at [docs.romm.app/4.8/](https://docs.romm.app/4.8/) as a froze Watershed release that made RomM a real multi-user platform: -- **Required authentication** — no more unauthenticated mode. -- **SQLite support dropped** — MariaDB became the default. -- **Redis built-in** — the experimental Redis add-on was absorbed. -- **Saves, states, and screenshots** — first-class user asset management. +- **Required authentication:** no more unauthenticated mode. +- **SQLite support dropped:** MariaDB became the default. +- **Redis built-in:** the experimental Redis add-on was absorbed. +- **Saves, states, and screenshots:** first-class user asset management. - **Config moved to a folder mount** (`/romm/config`). ## Older @@ -57,5 +57,5 @@ Watershed release that made RomM a real multi-user platform: ## Want release pings? -- Watch the [rommapp/romm](https://github.com/rommapp/romm) repo on GitHub — "Custom → Releases" for release-only notifications. +- Watch the [rommapp/romm](https://github.com/rommapp/romm) repo on GitHub: "Custom → Releases" for release-only notifications. - Join the [Discord](https://discord.gg/romm) and look for `#announcements`. diff --git a/docs/releases/index.md b/docs/releases/index.md index 4bde2557..1fda789a 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -11,23 +11,23 @@ RomM's current stable is [`5.0.0`](changelog.md). Read [What's New in 5.0](../ge ## Versioning -RomM follows **SemVer** for breaking changes and **CalVer-ish** for cadence — major versions are planned milestones, not scheduled. +RomM follows **SemVer** for breaking changes and **CalVer-ish** for cadence: major versions are planned milestones, not scheduled. -- **Patch releases** (`5.0.1`, `5.0.2`) — bug fixes only. Safe to pull automatically. No migration. -- **Minor releases** (`5.1.0`, `5.2.0`) — additive features. Schema may migrate automatically via Alembic; no action required beyond reading the release notes. -- **Major releases** (`5.0.0`, `6.0.0`) — breaking changes. Read the migration guide before upgrading. +- **Patch releases** (`5.0.1`, `5.0.2`): bug fixes only. Safe to pull automatically. No migration. +- **Minor releases** (`5.1.0`, `5.2.0`): additive features. Schema may migrate automatically via Alembic; no action required beyond reading the release notes. +- **Major releases** (`5.0.0`, `6.0.0`): breaking changes. Read the migration guide before upgrading. ## Image tags and what to pin | Tag | What it moves to | When to use | | --- | --- | --- | -| `rommapp/romm:latest` | Every new stable release | Dev / "I'll deal with it" setups. **Never pin production to `:latest`** — you'll ship untested upgrades. | -| `rommapp/romm:5.0.0` | Immutable — specific release | Production. Update deliberately by bumping the tag. | -| `rommapp/romm:5` | Latest in the 5.x line | Middle ground — auto-minor-upgrades within a major. | +| `rommapp/romm:latest` | Every new stable release | Dev / "I'll deal with it" setups. **Never pin production to `:latest`**; you'll ship untested upgrades. | +| `rommapp/romm:5.0.0` | Immutable, specific release | Production. Update deliberately by bumping the tag. | +| `rommapp/romm:5` | Latest in the 5.x line | Middle ground: auto-minor-upgrades within a major. | | `rommapp/romm:develop` | Every push to `master` | Don't. | | `rommapp/romm:5.0.0-slim` | Same as `5.0.0` but without EmulatorJS/Ruffle | Headless / API-only deployments. See [Image Variants](../install/image-variants.md). | -Registries: Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`) — same tags, same content. +Registries: Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`); same tags, same content. ## Docs versions @@ -43,23 +43,23 @@ We support the current major and the previous major for critical bug fixes and s | Major | Status | Frozen docs | | --- | --- | --- | -| **5.x** | **Current** — active development | `docs.romm.app/latest/` | +| **5.x** | **Current**, active development | `docs.romm.app/latest/` | | **4.x** | Security + critical bugs only | `docs.romm.app/4.8/` | -| **3.x** | Unsupported — upgrade | — | -| **≤2.x** | Unsupported | — | +| **3.x** | Unsupported, upgrade | N/A | +| **≤2.x** | Unsupported | N/A | Older frozen docs are retained for 12 months after the major's support window ends, then removed. Plan upgrades accordingly. ## Migration guides -- **[Upgrading to 5.0](upgrading-to-5.0.md)** — 4.x → 5.0. Required reading. -- **[Upgrading to 3.0](upgrading-to-3.0.md)** — 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical — keep for 2.x migrators. +- **[Upgrading to 5.0](upgrading-to-5.0.md):** 4.x → 5.0. Required reading. +- **[Upgrading to 3.0](upgrading-to-3.0.md):** 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical; keep for 2.x migrators. ## Where releases are announced -- [GitHub Releases](https://github.com/rommapp/romm/releases) — authoritative changelog, tag-by-tag. -- [Discord](https://discord.gg/romm) `#announcements` — release pings. -- [Changelog](changelog.md) — human-readable per-release summary. +- [GitHub Releases](https://github.com/rommapp/romm/releases): authoritative changelog, tag-by-tag. +- [Discord](https://discord.gg/romm) `#announcements`: release pings. +- [Changelog](changelog.md): human-readable per-release summary. ## Upgrade protocol diff --git a/docs/releases/upgrading-to-3.0.md b/docs/releases/upgrading-to-3.0.md index 56ea9e7a..296b44b8 100644 --- a/docs/releases/upgrading-to-3.0.md +++ b/docs/releases/upgrading-to-3.0.md @@ -1,6 +1,6 @@ --- title: Upgrading to 3.0 -description: Historical migration guide for 2.x → 3.0 — SQLite drop, auth required, Redis built-in, config folder mount, assets volume. +description: Historical migration guide for 2.x → 3.0: SQLite drop, auth required, Redis built-in, config folder mount, assets volume. --- # Upgrading to 3.0 @@ -8,15 +8,15 @@ description: Historical migration guide for 2.x → 3.0 — SQLite drop, auth re !!! info "This is a historical guide" RomM 3.0 shipped a while ago. If you're on 2.x, still use this page. If you're on 3.x / 4.x, see [Upgrading to 5.0](upgrading-to-5.0.md). -Version 3.0 introduced several breaking changes aimed at improving performance and unlocking new features (EmulatorJS, in-browser saves/states). Read this entire page before pulling the new image — skipping steps will make RomM inaccessible or unresponsive. +Version 3.0 introduced several breaking changes aimed at improving performance and unlocking new features (EmulatorJS, in-browser saves/states). Read this entire page before pulling the new image; skipping steps will make RomM inaccessible or unresponsive. All the changes below are reflected in the example [docker-compose.yml](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml), which was significantly simplified in 3.0. ## Dropped SQLite support -SQLite was removed because of ongoing engineering issues; MariaDB is stabler and the only supported default. If you were on SQLite, RomM will auto-migrate your data to MariaDB — **but you need to make the following changes before upgrading**. +SQLite was removed because of ongoing engineering issues; MariaDB is stabler and the only supported default. If you were on SQLite, RomM will auto-migrate your data to MariaDB, **but you need to make the following changes before upgrading**. -In your environment variables, set `ROMM_DB_DRIVER` to `mariadb` (or drop the variable entirely — it's no longer required) and add: +In your environment variables, set `ROMM_DB_DRIVER` to `mariadb` (or drop the variable entirely, it's no longer required) and add: ```yaml environment: @@ -42,7 +42,7 @@ We know this breaks the "unrestricted sharing" pattern some users relied on. See ## Redis is built in -The 2.x "experimental Redis" add-on container is gone — RomM ships with Redis internally. Remove the external Redis container and these environment variables: +The 2.x "experimental Redis" add-on container is gone; RomM ships with Redis internally. Remove the external Redis container and these environment variables: ```yaml # Remove: @@ -51,7 +51,7 @@ The 2.x "experimental Redis" add-on container is gone — RomM ships with Redis # - REDIS_PORT ``` -If you want an external Redis instance anyway (recommended for production), keep `REDIS_HOST` / `REDIS_PORT` — see [Redis or Valkey](../install/redis-or-valkey.md). +If you want an external Redis instance anyway (recommended for production), keep `REDIS_HOST` / `REDIS_PORT`; see [Redis or Valkey](../install/redis-or-valkey.md). ## Configuration folder @@ -68,7 +68,7 @@ Updated example: [config.example.yml](https://github.com/rommapp/romm/blob/maste ## New `/romm/assets` volume -3.0 introduced save, state, and screenshot management. Uploads land in `/romm/assets` inside the container — mount a host path so they persist: +3.0 introduced save, state, and screenshot management. Uploads land in `/romm/assets` inside the container; mount a host path so they persist: ```yaml volumes: @@ -79,9 +79,9 @@ Put this next to the folder you mount as `/romm/library` so all RomM-owned data ## After the upgrade -- Visit the web UI. You'll see a Setup Wizard if you had auth disabled previously — complete it to create the first admin. +- Visit the web UI. You'll see a Setup Wizard if you had auth disabled previously; complete it to create the first admin. - Run a scan to re-match any games that lost metadata during the SQLite → MariaDB migration. -- Start using saves/states — they'll live under `/romm/assets` from now on. +- Start using saves/states; they'll live under `/romm/assets` from now on. ## Rollback diff --git a/docs/releases/upgrading-to-5.0.md b/docs/releases/upgrading-to-5.0.md index 9229773f..bf283d69 100644 --- a/docs/releases/upgrading-to-5.0.md +++ b/docs/releases/upgrading-to-5.0.md @@ -1,6 +1,6 @@ --- title: Upgrading to 5.0 -description: Migrate from RomM 4.x to 5.0 — breaking changes, env var renames, pre-flight checklist, and rollback. +description: Migrate from RomM 4.x to 5.0: breaking changes, env var renames, pre-flight checklist, and rollback. --- # Upgrading to 5.0 @@ -11,23 +11,23 @@ Migration guide for **RomM 4.x → 5.0**. If you're on 2.x or earlier, upgrade t 5.0 is a major release with schema migrations and a handful of breaking changes. Skipping the pre-flight checklist will lose data or break your instance. !!! note "This guide is finalised at 5.0 GA" - Some sections below reference exact env-var renames and schema changes that will be confirmed against the final 5.0.0 source tag. If you're reading this before 5.0 GA, treat it as a structural outline — the commands are correct, specific rename lists will be filled in as the release tag is cut. + Some sections below reference exact env-var renames and schema changes that will be confirmed against the final 5.0.0 source tag. If you're reading this before 5.0 GA, treat it as a structural outline; the commands are correct, specific rename lists will be filled in as the release tag is cut. ## What changes in 5.0 ### Summary -- **Schema migration** — Alembic runs automatically on startup. Irreversible without a backup. +- **Schema migration:** Alembic runs automatically on startup. Irreversible without a backup. - **New env vars**, some renamed (see [Env var migration table](#env-var-migration-table)). -- **`config.yml` gains new sections** — `scan.region`, `scan.language`, `scan.media`, `scan.gamelist.export`, `scan.pegasus.export`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings keep working untouched. -- **Image tags add `:slim` variants** — no change to existing `:latest` / `:5.0.0`. -- **Docs URL structure** — every old URL redirects automatically. External links continue working. +- **`config.yml` gains new sections:** `scan.region`, `scan.language`, `scan.media`, `scan.gamelist.export`, `scan.pegasus.export`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings keep working untouched. +- **Image tags add `:slim` variants:** no change to existing `:latest` / `:5.0.0`. +- **Docs URL structure:** every old URL redirects automatically. External links continue working. ### Big new features -Highlights only — full list in [What's New in 5.0](../getting-started/what-is-new-in-5.md): +Highlights only; full list in [What's New in 5.0](../getting-started/what-is-new-in-5.md): -- Console Mode (`/console` — TV/gamepad UI). +- Console Mode (`/console`, TV/gamepad UI). - Smart and Virtual Collections. - ROM Patcher. - Netplay with ICE servers. @@ -90,7 +90,7 @@ Commit this to version control if you track your compose file. Saves guesswork o ### 5. Block access while you upgrade -Optional but recommended for shared instances — take RomM offline for ~5 minutes: +Optional but recommended for shared instances; take RomM offline for ~5 minutes: ```sh # Return 503 at the reverse proxy while upgrading @@ -130,22 +130,22 @@ docker compose logs -f romm What to watch for: -- **Alembic migrations running** — lines prefixed `INFO [alembic.runtime.migration]`. Takes anywhere from seconds (small libraries) to a couple minutes (very large ones). -- **`Application startup complete.`** — RomM is healthy. -- **`Watcher started.`** — filesystem watcher up (if enabled). -- **ERROR lines** — bad; go to [Rollback](#rollback). +- **Alembic migrations running:** lines prefixed `INFO [alembic.runtime.migration]`. Takes anywhere from seconds (small libraries) to a couple minutes (very large ones). +- **`Application startup complete.`** RomM is healthy. +- **`Watcher started.`** Filesystem watcher up (if enabled). +- **ERROR lines:** bad; go to [Rollback](#rollback). ### 4. Smoke-test Don't trust the migration until you've verified: - Log in as an Admin. -- **Administration → Server Stats** — counts look reasonable; no massive loss. +- **Administration → Server Stats:** counts look reasonable; no massive loss. - Open a platform with lots of games; check a few games have metadata. -- Open a game's details — Personal tab shows your rating / playtime if you had any. -- Check **Administration → Users** — your accounts are intact. -- Run a **Quick** scan — should complete cleanly in a few seconds. -- (If you had OIDC) log out, log in via OIDC — should pick up `OIDC_CLAIM_ROLES` if configured. +- Open a game's details; Personal tab shows your rating / playtime if you had any. +- Check **Administration → Users**; your accounts are intact. +- Run a **Quick** scan; should complete cleanly in a few seconds. +- (If you had OIDC) log out, log in via OIDC; should pick up `OIDC_CLAIM_ROLES` if configured. ## Env var migration table @@ -172,14 +172,14 @@ Full list in [Environment Variables](../reference/environment-variables.md). The `config.yml` gains new sections; everything else stays backwards compatible. -- **`scan.region`** — region preference order (array). -- **`scan.language`** — language preference order (array). -- **`scan.media`** — which media types to fetch (`[box2d, screenshot, manual]`, etc.). -- **`scan.gamelist.export`** + **`scan.gamelist.media.*`** — export gamelist.xml for ES-DE/Batocera. -- **`scan.pegasus.export`** — export metadata.pegasus.txt for Pegasus. -- **`filesystem.firmware_folder`** — override the `bios` folder name. -- **`emulatorjs.netplay.ice_servers`** — STUN/TURN for Netplay. -- **`emulatorjs.settings`** / **`emulatorjs.controls`** — per-core emulator config and button mappings. +- **`scan.region`:** region preference order (array). +- **`scan.language`:** language preference order (array). +- **`scan.media`:** which media types to fetch (`[box2d, screenshot, manual]`, etc.). +- **`scan.gamelist.export`** + **`scan.gamelist.media.*`:** export gamelist.xml for ES-DE/Batocera. +- **`scan.pegasus.export`:** export metadata.pegasus.txt for Pegasus. +- **`filesystem.firmware_folder`:** override the `bios` folder name. +- **`emulatorjs.netplay.ice_servers`:** STUN/TURN for Netplay. +- **`emulatorjs.settings`** / **`emulatorjs.controls`:** per-core emulator config and button mappings. - **`emulatorjs.cache_limit`** / **`emulatorjs.disable_batch_bootup`** / **`emulatorjs.disable_auto_unload`**. Full schema: [Configuration File](../reference/configuration-file.md). @@ -193,7 +193,7 @@ The docs site restructured in 5.0. Every old URL redirects to its new home: - `/latest/Usage/UserManagement/` → `/latest/administration/users-and-roles/` - `/latest/OIDC-Guides/OIDC-Setup-With-Keycloak/` → `/latest/administration/oidc/keycloak/` -And so on — every page in the 4.x IA is covered. External links in forum posts, blog articles, issue threads, and Discord messages keep working. Internal bookmarks to `/latest///` hit a 301 to the new slug automatically. +And so on; every page in the 4.x IA is covered. External links in forum posts, blog articles, issue threads, and Discord messages keep working. Internal bookmarks to `/latest///` hit a 301 to the new slug automatically. ## Rollback @@ -207,7 +207,7 @@ docker compose down ### 2. Revert the image tag -Edit `docker-compose.yml` — change `rommapp/romm:5.0.0` back to whatever you were on (`rommapp/romm:4.8.1`). +Edit `docker-compose.yml`: change `rommapp/romm:5.0.0` back to whatever you were on (`rommapp/romm:4.8.1`). ### 3. Restore the DB @@ -246,7 +246,7 @@ Open a bug report on [GitHub](https://github.com/rommapp/romm/issues) with: ### Migrations hang -Watch `docker logs -f romm`. If Alembic is still running, it's still running — large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: +Watch `docker logs -f romm`. If Alembic is still running, it's still running; large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: - DB connection issues (`docker logs romm-db`). - Out-of-memory kills (`dmesg | grep -i oom`). @@ -254,7 +254,7 @@ Watch `docker logs -f romm`. If Alembic is still running, it's still running — ### WebSockets fail after upgrade -If you're behind a reverse proxy, the upgrade shouldn't change anything — but it's a good moment to verify the proxy forwards `Upgrade: websocket` properly. See [Reverse Proxy](../install/reverse-proxy.md). +If you're behind a reverse proxy, the upgrade shouldn't change anything, but it's a good moment to verify the proxy forwards `Upgrade: websocket` properly. See [Reverse Proxy](../install/reverse-proxy.md). ### OIDC users lose role, come back as Viewer @@ -266,13 +266,13 @@ See [OIDC Setup → Role mapping](../administration/oidc/index.md#role-mapping-5 ### Scheduled tasks stop firing -5.0 renamed several cron env vars. Check [Environment Variables](../reference/environment-variables.md) and update your env to the new names — otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). +5.0 renamed several cron env vars. Check [Environment Variables](../reference/environment-variables.md) and update your env to the new names; otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). ## After the upgrade Once everything's stable: - Pin your image to `rommapp/romm:5.0.0` in version control. -- Update your uptime monitor's heartbeat endpoint — still `/api/heartbeat`, but now exposes more info (see [Observability](../administration/observability.md)). +- Update your uptime monitor's heartbeat endpoint: still `/api/heartbeat`, but now exposes more info (see [Observability](../administration/observability.md)). - Take a fresh backup of the post-migration state. You want a known-good 5.0 snapshot, not just the pre-5.0 one. - Explore the new stuff in [What's New in 5.0](../getting-started/what-is-new-in-5.md). diff --git a/docs/resources/snippets/env-vars.md b/docs/resources/snippets/env-vars.md index 688c645d..92e75141 100644 --- a/docs/resources/snippets/env-vars.md +++ b/docs/resources/snippets/env-vars.md @@ -1,4 +1,4 @@ - + ### Core Application diff --git a/docs/resources/snippets/scheduled-tasks.md b/docs/resources/snippets/scheduled-tasks.md index 3d4f719e..1f5f98a7 100644 --- a/docs/resources/snippets/scheduled-tasks.md +++ b/docs/resources/snippets/scheduled-tasks.md @@ -1,4 +1,4 @@ - + | Task | Type | Default schedule | Env var | Purpose | | --- | --- | --- | --- | --- | @@ -9,7 +9,7 @@ | RetroAchievements Sync | Scheduled | `0 4 * * *` | `RETROACHIEVEMENTS_SYNC_INTERVAL_CRON` | Sync per-user RetroAchievements progression data. | | Netplay Cleanup | Scheduled | `*/30 * * * *` | `NETPLAY_CLEANUP_INTERVAL_CRON` | Remove orphaned netplay sessions. | | Push-Pull Device Sync | Scheduled | `*/15 * * * *` | `PUSH_PULL_SYNC_INTERVAL_CRON` | Bidirectional save/state sync to registered devices. | -| Cleanup Missing ROMs | Manual | `—` | `—` | Remove DB entries whose files are no longer on disk. | -| Cleanup Orphaned Resources | Manual | `—` | `—` | Delete cached media not referenced by any ROM. | -| Sync Folder Scan | Manual | `—` | `—` | On-demand library scan + sync. | -| Filesystem Watcher | Watcher | `—` | `WATCHER_ENABLED` | Live-watch the library folder; trigger quick scans on changes. | +| Cleanup Missing ROMs | Manual | `-` | `-` | Remove DB entries whose files are no longer on disk. | +| Cleanup Orphaned Resources | Manual | `-` | `-` | Delete cached media not referenced by any ROM. | +| Sync Folder Scan | Manual | `-` | `-` | On-demand library scan + sync. | +| Filesystem Watcher | Watcher | `-` | `WATCHER_ENABLED` | Live-watch the library folder; trigger quick scans on changes. | diff --git a/docs/resources/snippets/supported-platforms.md b/docs/resources/snippets/supported-platforms.md index 8c9e0a41..9c841df4 100644 --- a/docs/resources/snippets/supported-platforms.md +++ b/docs/resources/snippets/supported-platforms.md @@ -1,4 +1,4 @@ - + diff --git a/docs/scripts/gen_env_vars.py b/docs/scripts/gen_env_vars.py index a107f636..3c072d1f 100644 --- a/docs/scripts/gen_env_vars.py +++ b/docs/scripts/gen_env_vars.py @@ -79,7 +79,7 @@ def parse(env_template: str) -> list[dict]: if not line.strip(): continue - # Variable line — has `=` and starts with an uppercase letter. + # Variable line: has `=` and starts with an uppercase letter. if "=" in line and line[:1].isalpha() and line[:1].isupper(): row = parse_var(line) if row: @@ -87,7 +87,7 @@ def parse(env_template: str) -> list[dict]: rows.append(row) continue - # Section header — comment line with no `=`. + # Section header: comment line with no `=`. if line.startswith("#"): text = line.lstrip("#").strip() if text and "=" not in text: @@ -102,7 +102,7 @@ def render(rows: Iterable[dict]) -> str: grouped[r["section"]].append(r) out: list[str] = [] - out.append("") + out.append("") out.append("") for section, items in grouped.items(): @@ -113,7 +113,7 @@ def render(rows: Iterable[dict]) -> str: for r in items: default = f"`{r['default']}`" if r["default"] else "" required = "`✓`" if r["required"] else "" - desc = r["description"] or "—" + desc = r["description"] or "-" out.append( f"| `{r['name']}` | {default} | {required} | {desc} |" ) diff --git a/docs/scripts/gen_platforms.py b/docs/scripts/gen_platforms.py index c4bdfa6c..539752e9 100644 --- a/docs/scripts/gen_platforms.py +++ b/docs/scripts/gen_platforms.py @@ -18,7 +18,7 @@ PLACEHOLDER = """\ - + diff --git a/docs/scripts/gen_scheduled_tasks.py b/docs/scripts/gen_scheduled_tasks.py index 0557df52..2f8669ad 100644 --- a/docs/scripts/gen_scheduled_tasks.py +++ b/docs/scripts/gen_scheduled_tasks.py @@ -71,28 +71,28 @@ { "name": "Cleanup Missing ROMs", "type": "Manual", - "default_cron": "—", - "env_var": "—", + "default_cron": "-", + "env_var": "-", "purpose": "Remove DB entries whose files are no longer on disk.", }, { "name": "Cleanup Orphaned Resources", "type": "Manual", - "default_cron": "—", - "env_var": "—", + "default_cron": "-", + "env_var": "-", "purpose": "Delete cached media not referenced by any ROM.", }, { "name": "Sync Folder Scan", "type": "Manual", - "default_cron": "—", - "env_var": "—", + "default_cron": "-", + "env_var": "-", "purpose": "On-demand library scan + sync.", }, { "name": "Filesystem Watcher", "type": "Watcher", - "default_cron": "—", + "default_cron": "-", "env_var": "WATCHER_ENABLED", "purpose": "Live-watch the library folder; trigger quick scans on changes.", }, @@ -101,7 +101,7 @@ def render() -> str: out = [ - "", + "", "", "| Task | Type | Default schedule | Env var | Purpose |", "| --- | --- | --- | --- | --- |", diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index b0ef5b0e..ba547080 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -18,11 +18,11 @@ Fix: [clear cookies](https://support.google.com/accounts/answer/32050) for the R CSRF protection is on by default. A mismatched or missing `csrftoken` cookie causes this. -1. Reload the page — RomM sets a fresh CSRF cookie on GET requests, which should fix it on the next POST. +1. Reload the page; RomM sets a fresh CSRF cookie on GET requests, which should fix it on the next POST. 2. Still broken? Clear cookies for the RomM host and hard-reload (`CMD+SHIFT+R` / `CTRL+F5`). 3. Known to happen on Chrome specifically; rare on Firefox/Safari. -If you're behind a reverse proxy and CSRF keeps failing, the proxy is probably stripping the `csrftoken` cookie or the `X-CSRFToken` header. See the [Reverse Proxy recipes](../install/reverse-proxy.md) — every one of them forwards `Cookie` and all custom headers by default, so if yours doesn't, fix the proxy config. +If you're behind a reverse proxy and CSRF keeps failing, the proxy is probably stripping the `csrftoken` cookie or the `X-CSRFToken` header. See the [Reverse Proxy recipes](../install/reverse-proxy.md); every one of them forwards `Cookie` and all custom headers by default, so if yours doesn't, fix the proxy config. ## `400 Bad Request` on the WebSocket endpoint @@ -30,10 +30,10 @@ Your reverse proxy is stripping the WebSocket upgrade. RomM uses Socket.IO for l Fixes per proxy: -- **Nginx / NPM** — enable WebSockets Support; the [Reverse Proxy](../install/reverse-proxy.md) snippets already do this. -- **Traefik** — add `proxy_set_header Upgrade $http_upgrade` (or use the Traefik middleware equivalent). -- **Caddy** — WebSockets work out of the box with `reverse_proxy`. -- **Cloudflare** — enable **WebSockets** under Network settings. +- **Nginx / NPM**: enable WebSockets Support; the [Reverse Proxy](../install/reverse-proxy.md) snippets already do this. +- **Traefik**: add `proxy_set_header Upgrade $http_upgrade` (or use the Traefik middleware equivalent). +- **Caddy**: WebSockets work out of the box with `reverse_proxy`. +- **Cloudflare**: enable **WebSockets** under Network settings. ## `Error: Could not get twitch auth token: check client_id and client_secret` @@ -52,7 +52,7 @@ You set `DISABLE_USERPASS_LOGIN=true` and now OIDC isn't working. 1. Edit your compose / env to unset `DISABLE_USERPASS_LOGIN` (or set it to `false`). 2. `docker compose up -d` to restart with the new config. 3. Log in with your local admin. -4. Fix OIDC — see below. +4. Fix OIDC; see below. 5. Re-enable `DISABLE_USERPASS_LOGIN` only after confirming OIDC works end-to-end. This is the reason [OIDC Setup](../administration/oidc/index.md) tells you to verify OIDC before turning off local login. @@ -80,7 +80,7 @@ You configured `OIDC_CLAIM_ROLES` but RomM isn't honouring it. 2. **Does the value match?** `OIDC_ROLE_ADMIN=romm-admin` will only match if the claim contains exactly the string `romm-admin`. Case-sensitive. 3. **Is the claim mapper on the IdP side configured to include the claim?** On Keycloak, for example, you need a Client Scope with a Group Membership mapper added to the client. -Roles are re-evaluated on every login — there's no cache to bust. Log out and back in after fixing. +Roles are re-evaluated on every login; there's no cache to bust. Log out and back in after fixing. ### "Email is missing from token" (Zitadel-specific) @@ -120,5 +120,5 @@ If `bypass_autologin` doesn't work in your version, shell into the container and ## Still stuck - Check the container logs: `docker logs romm 2>&1 | grep -iE 'auth|oidc|oauth'`. -- Cross-reference your IdP's audit logs — they often show exactly why a login was rejected on their side. +- Cross-reference your IdP's audit logs; they often show exactly why a login was rejected on their side. - Ask on [Discord](https://discord.gg/romm) `#help` with the IdP name and the exact error text. diff --git a/docs/troubleshooting/in-browser-play.md b/docs/troubleshooting/in-browser-play.md index bfb600fb..c35526c1 100644 --- a/docs/troubleshooting/in-browser-play.md +++ b/docs/troubleshooting/in-browser-play.md @@ -1,6 +1,6 @@ --- title: In-Browser Play Troubleshooting -description: Diagnose EmulatorJS and Ruffle issues — cores not loading, BIOS missing, audio, performance. +description: Diagnose EmulatorJS and Ruffle issues: cores not loading, BIOS missing, audio, performance. --- # In-Browser Play Troubleshooting @@ -12,8 +12,8 @@ Symptom: Play button spins forever or shows an error immediately. Checks in order: 1. **Is it the slim image?** `ENABLE_EMULATORJS=true` on the slim image doesn't magically include EmulatorJS. Either switch to `rommapp/romm:` (full) or disable in-browser play. See [Image Variants](../install/image-variants.md). -2. **Browser console** — open devtools, look for 404s on `/assets/emulatorjs/...`. Indicates the EmulatorJS bundle didn't install correctly in the container. `docker logs romm` may show the entrypoint's install step failing. -3. **Browser compatibility** — EmulatorJS uses SharedArrayBuffer. Needs a modern Chrome/Firefox/Safari and an HTTPS-served RomM (cross-origin isolation requires HTTPS). If you're still on plain HTTP for some reason, TLS it — see [Reverse Proxy](../install/reverse-proxy.md). +2. **Browser console**: open devtools, look for 404s on `/assets/emulatorjs/...`. Indicates the EmulatorJS bundle didn't install correctly in the container. `docker logs romm` may show the entrypoint's install step failing. +3. **Browser compatibility**: EmulatorJS uses SharedArrayBuffer. Needs a modern Chrome/Firefox/Safari and an HTTPS-served RomM (cross-origin isolation requires HTTPS). If you're still on plain HTTP for some reason, TLS it; see [Reverse Proxy](../install/reverse-proxy.md). ## Black screen, no audio @@ -36,7 +36,7 @@ Specific examples: Not sure which files? Error message usually names them. Or check [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/). -Multi-file firmware — zip them together and upload as a single `firmware.zip`. EmulatorJS unpacks automatically. +Multi-file firmware: zip them together and upload as a single `firmware.zip`. EmulatorJS unpacks automatically. ## Controls do nothing @@ -61,24 +61,24 @@ Multi-file firmware — zip them together and upload as a single `firmware.zip`. Different cores use different hotkeys: -- **PC keyboard** — often F1, F9, or `~` (tilde). Some cores use ESC. -- **Gamepad** — usually Select + Start simultaneously. +- **PC keyboard**: often F1, F9, or `~` (tilde). Some cores use ESC. +- **Gamepad**: usually Select + Start simultaneously. Rebind via Profile → User Interface (the operator-side overrides live in [`emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols) in `config.yml`). ## DOS games fail to boot -- **autorun.bat issues** — turn on [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjsdisable_batch_bootup) in `config.yml`. -- **Sound card config wrong** — dosbox-pure tries to auto-detect; may need manual tweaking. In-game Menu → **Settings** → set Sound Blaster port. -- **Game needs specific CPU speed** — some DOS games are CPU-bound. Slow down via dosbox-pure settings. +- **autorun.bat issues**: turn on [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjsdisable_batch_bootup) in `config.yml`. +- **Sound card config wrong**: dosbox-pure tries to auto-detect; may need manual tweaking. In-game Menu → **Settings** → set Sound Blaster port. +- **Game needs specific CPU speed**: some DOS games are CPU-bound. Slow down via dosbox-pure settings. See the [MS-DOS platform guide](../platforms/ms-dos.md) for deeper DOS-specific notes. ## Ruffle games -- **"File not Flash"** — confirm the file extension is `.swf`. Ruffle only handles Flash SWF. +- **"File not Flash"**: confirm the file extension is `.swf`. Ruffle only handles Flash SWF. - **Wrong platform folder.** Ruffle only plays from `flash/` or `browser/` folders. See [Ruffle setup](../using/in-browser-play.md#ruffle). -- **AS3 game crashes** — Ruffle's ActionScript 3 support is in progress. Some games won't work cleanly yet. [Ruffle compatibility list](https://ruffle.rs/#compatibility). +- **AS3 game crashes**: Ruffle's ActionScript 3 support is in progress. Some games won't work cleanly yet. [Ruffle compatibility list](https://ruffle.rs/#compatibility). ## Performance on mobile @@ -88,12 +88,12 @@ In-browser emulation is CPU-heavy. Mobile tips: - **Use Chrome on Android, Safari on iOS.** Other browsers are measurably slower for WASM. - **Close other tabs.** - **Try a lighter core.** `snes9x` over `bsnes`, `fceumm` over `nestopia`, etc. -- **Consider a native app.** [Argosy Launcher](../ecosystem/argosy.md) on Android uses native emulators — orders of magnitude more efficient. +- **Consider a native app.** [Argosy Launcher](../ecosystem/argosy.md) on Android uses native emulators, which are orders of magnitude more efficient. ## Fullscreen oddities -- **Notch / cutout clipping on mobile** — iPhone notches and Android cutouts can eat corners of the emulator viewport. Rotate to landscape before going fullscreen, or toggle Profile → User Interface → Fullscreen strategy. -- **Browser escapes fullscreen after idle** — browser security behaviour. Can't be worked around. +- **Notch / cutout clipping on mobile**: iPhone notches and Android cutouts can eat corners of the emulator viewport. Rotate to landscape before going fullscreen, or toggle Profile → User Interface → Fullscreen strategy. +- **Browser escapes fullscreen after idle**: browser security behaviour. Can't be worked around. ## Known limitations by core @@ -107,8 +107,8 @@ In-browser emulation is CPU-heavy. Mobile tips: ## Still stuck -- `docker logs romm | grep -i emulator` — server-side clues. -- Browser devtools Console — client-side clues. -- [Discord](https://discord.gg/romm) `#help` — include the ROM file, the core you tried, the exact error. +- `docker logs romm | grep -i emulator`: server-side clues. +- Browser devtools Console: client-side clues. +- [Discord](https://discord.gg/romm) `#help`: include the ROM file, the core you tried, the exact error. For Netplay-specific issues: [Netplay Troubleshooting](netplay.md). diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md index e419f08b..06bffcec 100644 --- a/docs/troubleshooting/index.md +++ b/docs/troubleshooting/index.md @@ -11,7 +11,7 @@ Something's broken. Start with the symptom that best matches: ### RomM won't start -- Container crashes immediately → check `docker logs romm`. If it's `invalid host in "tcp://..."`, you're on Kubernetes — see [Kubernetes Troubleshooting](kubernetes.md). +- Container crashes immediately → check `docker logs romm`. If it's `invalid host in "tcp://..."`, you're on Kubernetes; see [Kubernetes Troubleshooting](kubernetes.md). - Database connection errors → verify `DB_HOST` / `DB_PASSWD` match your DB container, and that the DB has finished initialising (first run takes longer than you'd think). - "Page not found" on first load → wait; initial migrations and resource seeding take a minute. @@ -38,12 +38,12 @@ Something's broken. Start with the symptom that best matches: ### Platform-specific -- [Synology Troubleshooting](synology.md) — permission errors, DSM gotchas. -- [Kubernetes Troubleshooting](kubernetes.md) — the `enableServiceLinks` fix and related. -- [Miscellaneous Troubleshooting](miscellaneous.md) — everything else. +- [Synology Troubleshooting](synology.md): permission errors, DSM gotchas. +- [Kubernetes Troubleshooting](kubernetes.md): the `enableServiceLinks` fix and related. +- [Miscellaneous Troubleshooting](miscellaneous.md): everything else. ## Still stuck -- [GitHub issues](https://github.com/rommapp/romm/issues) — search first; open if you've got a reproducible bug. -- [Discord](https://discord.gg/romm) — `#help` channel, staffed by community + maintainers. +- [GitHub issues](https://github.com/rommapp/romm/issues): search first; open if you've got a reproducible bug. +- [Discord](https://discord.gg/romm): `#help` channel, staffed by community + maintainers. - Logs to include when asking for help: `docker logs romm` (redact secrets), your RomM version (top of the About modal in the profile drawer), and the exact steps you took. diff --git a/docs/troubleshooting/kubernetes.md b/docs/troubleshooting/kubernetes.md index 58501b60..96c09d85 100644 --- a/docs/troubleshooting/kubernetes.md +++ b/docs/troubleshooting/kubernetes.md @@ -27,7 +27,7 @@ Covered in full in the [Kubernetes install guide](../install/kubernetes.md#requi ## Large uploads rejected with `413 Request Entity Too Large` -The ingress controller is capping request body size. Default for nginx-ingress is 1 MB — won't survive a single ROM upload. +The ingress controller is capping request body size. Default for nginx-ingress is 1 MB, which won't survive a single ROM upload. Add the annotation: @@ -82,13 +82,13 @@ Two fixes: runAsUser: 0 ``` -- **Storage class that supports `fsGroup`** — add `fsGroup: 1000` to the pod's `securityContext`. Works on most CSI drivers but not all. +- **Storage class that supports `fsGroup`**: add `fsGroup: 1000` to the pod's `securityContext`. Works on most CSI drivers but not all. ## Pod can reach the DB but RomM crashes with `ConnectionRefused` Startup ordering. RomM starts before the DB is ready, fails, and crashlooped-restarts forever because the restart is too fast for the DB to catch up. -Fix: add an init container that waits, or a `readinessProbe` + generous `startupProbe` on the DB StatefulSet. The [Kubernetes install guide](../install/kubernetes.md#mariadb) has a readiness probe baked in — check you're using it. +Fix: add an init container that waits, or a `readinessProbe` + generous `startupProbe` on the DB StatefulSet. The [Kubernetes install guide](../install/kubernetes.md#mariadb) has a readiness probe baked in; check you're using it. ## Scheduler tasks don't run @@ -117,7 +117,7 @@ resources: memory: "4Gi" ``` -Or disable hashing on the Scan page to cut memory use by ~80% (you lose RetroAchievements + Hasheous matching — see [Metadata Providers](../administration/metadata-providers.md)). +Or disable hashing on the Scan page to cut memory use by ~80% (you lose RetroAchievements + Hasheous matching; see [Metadata Providers](../administration/metadata-providers.md)). ## Still stuck diff --git a/docs/troubleshooting/miscellaneous.md b/docs/troubleshooting/miscellaneous.md index f48b33f6..e6169600 100644 --- a/docs/troubleshooting/miscellaneous.md +++ b/docs/troubleshooting/miscellaneous.md @@ -18,7 +18,7 @@ volumes: This only applies to SQLite. MariaDB / MySQL / Postgres users persist via the DB container's own volume (`mysql_data:/var/lib/mysql` and similar), not via a `/romm/database` mount. -For anything multi-user or larger than a few hundred games, **don't use SQLite** — see [Databases](../install/databases.md). +For anything multi-user or larger than a few hundred games, **don't use SQLite**; see [Databases](../install/databases.md). ## `Could not get twitch auth token: check client_id and client_secret` @@ -61,7 +61,7 @@ docker logs romm 2>&1 | grep -iE 'database|redis' Open devtools → Network tab → reload. You're looking for: -- **404 on `/assets/index-*.js`** — nginx inside the container is misrouting. Restart the container. +- **404 on `/assets/index-*.js`**: nginx inside the container is misrouting. Restart the container. - **WebSocket connection failed** → reverse proxy isn't forwarding WebSockets. See [Authentication Troubleshooting → WebSockets](authentication.md#400-bad-request-on-the-websocket-endpoint). - **CORS error** → `ROMM_BASE_URL` doesn't match the URL you're actually accessing. Set it correctly and restart. @@ -69,16 +69,16 @@ Open devtools → Network tab → reload. You're looking for: Static media is served from `/romm/resources` through nginx. If images 404: -1. **Mount is wrong** — `docker exec romm ls /romm/resources` should show cached files after at least one scan. -2. **Permissions** — same fix pattern as [Synology permissions](synology.md). -3. **Scan hasn't run yet** — covers are fetched during scan. Run one. +1. **Mount is wrong**: `docker exec romm ls /romm/resources` should show cached files after at least one scan. +2. **Permissions**: same fix pattern as [Synology permissions](synology.md). +3. **Scan hasn't run yet**: covers are fetched during scan. Run one. ## RomM is slow Most common causes, in order: 1. **Hashing large files on spinning disks.** Check **Skip hash calculation** or move the library to SSD. -2. **SQLite with many users.** Switch to MariaDB or Postgres — see [Databases](../install/databases.md). +2. **SQLite with many users.** Switch to MariaDB or Postgres; see [Databases](../install/databases.md). 3. **Metadata provider rate limiting during scans.** Reduce `SCAN_WORKERS` or split scans by platform. 4. **Container on underpowered hardware.** Check CPU/RAM headroom with `docker stats romm`. If RAM is pegged, raise container limits or disable `WATCHER_ENABLED`. @@ -88,10 +88,10 @@ Most common causes, in order: 2. Restore your DB dump from [Backup & Restore](../install/backup-and-restore.md). 3. Open a bug report with: old version, new version, the exact error or behaviour, and `docker logs romm` from the failed startup. -For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md) — the migration guide covers every known breaking change. +For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md); the migration guide covers every known breaking change. ## It's not listed here -- [Discord](https://discord.gg/romm) `#help` — active, friendly, fast. -- [GitHub Issues](https://github.com/rommapp/romm/issues) — for reproducible bugs. +- [Discord](https://discord.gg/romm) `#help`: active, friendly, fast. +- [GitHub Issues](https://github.com/rommapp/romm/issues): for reproducible bugs. - Include your RomM version, deployment method, logs (secrets redacted), and exact repro steps. diff --git a/docs/troubleshooting/netplay.md b/docs/troubleshooting/netplay.md index e9ecf2b9..2886a46c 100644 --- a/docs/troubleshooting/netplay.md +++ b/docs/troubleshooting/netplay.md @@ -1,6 +1,6 @@ --- title: Netplay Troubleshooting -description: Fix EmulatorJS Netplay — rooms not appearing, failed to start, desync, lag. +description: Fix EmulatorJS Netplay issues: rooms not appearing, failed to start, desync, lag. --- # Netplay Troubleshooting @@ -15,7 +15,7 @@ The most common error. Almost always the operator-side config: 2. **Are ICE servers configured?** The operator needs at least one STUN server in `emulatorjs.netplay.ice_servers`. Without any, NAT traversal can't begin. 3. **ICE server URLs reachable?** RomM can't talk to `stun.l.google.com:19302` if your server has no outbound internet; sounds silly but happens in air-gapped labs. -Operator: check `docker logs romm | grep -i netplay` — errors usually point at ICE config or network access. +Operator: check `docker logs romm | grep -i netplay`; errors usually point at ICE config or network access. Full config: [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50). @@ -30,7 +30,7 @@ You created a room as host; other players don't see it. ## Joined but video never appears - **Host's browser stopped streaming.** Check host's tab is still foregrounded; browsers throttle background tabs. -- **NAT traversal failed.** STUN couldn't hole-punch, and you have no TURN configured. Add a TURN server — see [Netplay → ICE servers](../using/netplay.md#ice-servers-the-nat-stuff). +- **NAT traversal failed.** STUN couldn't hole-punch, and you have no TURN configured. Add a TURN server; see [Netplay → ICE servers](../using/netplay.md#ice-servers-the-nat-stuff). - **Symmetric NAT on one end.** Corporate networks and some carrier-grade NAT can't be STUN-traversed. Only TURN (relay) will work. ## High lag / input delay @@ -43,7 +43,7 @@ Normal Netplay delay is 50–150 ms. More than that: ## Desync (players see different game state) -Different players' screens diverge over time — you do a move, they don't see it. +Different players' screens diverge over time: you do a move, they don't see it. - **Browser tab backgrounded.** Browsers throttle. Keep both tabs foregrounded. - **Bandwidth starvation.** Video stream is dropping frames, inputs are queueing. Reduce resolution or lower the emulator frame rate. @@ -55,11 +55,11 @@ WebRTC audio is known to be fragile. Workarounds: - **Use Chrome.** Its WebRTC implementation is the most mature. - **Lower audio quality** in the in-game Menu → Audio. -- **Restart the session** — audio sometimes recovers only after a full room teardown. +- **Restart the session**; audio sometimes recovers only after a full room teardown. ## Room disappears after the host leaves -Expected. Rooms are ephemeral — when the host disconnects, the room is cleaned up (either immediately or by the next [Netplay Cleanup scheduled task](../administration/scheduled-tasks.md) sweep). +Expected. Rooms are ephemeral: when the host disconnects, the room is cleaned up (either immediately or by the next [Netplay Cleanup scheduled task](../administration/scheduled-tasks.md) sweep). If you want your session back, the host recreates the room and other players rejoin. @@ -67,19 +67,19 @@ If you want your session back, the host recreates the room and other players rej Netplay switches EmulatorJS to nightly-CDN assets. Sometimes the nightly is temporarily broken. -- **Temporary** — usually self-heals within a day. -- **Workaround** — operator can disable Netplay (`emulatorjs.netplay.enabled: false`) to go back to stable local assets. +- **Temporary**: usually self-heals within a day. +- **Workaround**: operator can disable Netplay (`emulatorjs.netplay.enabled: false`) to go back to stable local assets. See [In-Browser Play → Netplay](../using/netplay.md#known-caveat-nightly-cdn). ## Still stuck -- `docker logs romm | grep -i netplay` — server-side log. -- Browser devtools Console — client-side log. -- `chrome://webrtc-internals` — live ICE / WebRTC stats on Chrome. Shows exactly where ICE is failing. +- `docker logs romm | grep -i netplay`: server-side log. +- Browser devtools Console: client-side log. +- `chrome://webrtc-internals`: live ICE / WebRTC stats on Chrome. Shows exactly where ICE is failing. - [Discord](https://discord.gg/romm) `#help` with both sides' error text. ## See also -- [Using → Netplay](../using/netplay.md) — how Netplay works and how to configure it. -- [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50) — full config schema. +- [Using → Netplay](../using/netplay.md): how Netplay works and how to configure it. +- [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50): full config schema. diff --git a/docs/troubleshooting/scanning.md b/docs/troubleshooting/scanning.md index baee0186..d01359fc 100644 --- a/docs/troubleshooting/scanning.md +++ b/docs/troubleshooting/scanning.md @@ -11,24 +11,24 @@ Most scan problems boil down to: library not mounted where RomM expects, folder Three common causes: -1. **Library mounted in the wrong place.** RomM expects your library at `/romm/library` inside the container. Verify with `docker exec romm ls /romm/library` — you should see your `roms/` directory (Structure A) or platform folders (Structure B). +1. **Library mounted in the wrong place.** RomM expects your library at `/romm/library` inside the container. Verify with `docker exec romm ls /romm/library`; you should see your `roms/` directory (Structure A) or platform folders (Structure B). 2. **Permissions.** The RomM container has to read the files. Check from the host: `ls -lh /path/to/library`. If the RomM process can't see them, nothing scans. 3. **Invalid folder structure.** Review [Folder Structure](../getting-started/folder-structure.md). RomM tries Structure A first; if it finds neither layout, the scan completes in seconds with nothing found. ## "ROMs not found for platform X, check romm folder structure" -Same root cause as the previous one. RomM needs a `roms/` directory somewhere — either `/romm/library/roms/{platform}/` (Structure A) or `/romm/library/{platform}/roms/` (Structure B). +Same root cause as the previous one. RomM needs a `roms/` directory somewhere: either `/romm/library/roms/{platform}/` (Structure A) or `/romm/library/{platform}/roms/` (Structure B). Common mount-path mistakes: ```yaml -# WRONG — mounts the platform folder itself +# WRONG: mounts the platform folder itself - /server/media/games/snes:/romm/library -# WRONG — skips the roms/ layer entirely +# WRONG: skips the roms/ layer entirely - /server/media/games:/romm/library -# CORRECT — mounts the parent of roms/ +# CORRECT: mounts the parent of roms/ - /server/media/library:/romm/library # if library/ contains roms/ - /server/media/games/roms:/romm/library/roms # if you want to be explicit ``` @@ -56,7 +56,7 @@ Full list of supported slugs: [Supported Platforms](../platforms/supported-platf Scans are capped. If yours hits the cap: -1. **Use `Quick` mode** from the Scan page — skips already-catalogued files. Most repeated scans complete in minutes. +1. **Use `Quick` mode** from the Scan page: skips already-catalogued files. Most repeated scans complete in minutes. 2. **Raise the cap** if you need a full rescan: `SCAN_TIMEOUT_HOURS=8` (or whatever). See [Environment Variables](../reference/environment-variables.md). 3. **Run scans per-platform** instead of everything at once, to checkpoint progress. @@ -70,9 +70,9 @@ docker logs romm 2>&1 | grep -E 'ERROR.*scan_handler' Common culprits: -- **Corrupted file** — unzip it, re-zip, try again. -- **Old DOS zip with backslash paths** — Python's `zipfile` chokes on these. Re-create the archive with forward slashes. -- **Read errors on the mount** — usually SMB/NFS flakiness. Watch `dmesg` for mount drops. +- **Corrupted file**: unzip it, re-zip, try again. +- **Old DOS zip with backslash paths**: Python's `zipfile` chokes on these. Re-create the archive with forward slashes. +- **Read errors on the mount**: usually SMB/NFS flakiness. Watch `dmesg` for mount drops. Log lines look like: @@ -119,10 +119,10 @@ Options, in order of effort: Hashing large ROMs (PS1, Saturn, DC images) is IO-bound. Options: -- **Skip hashing on small hosts** — check **Skip hash calculation** on the Scan page, or set `filesystem.skip_hash_calculation: true` in `config.yml`. You lose RetroAchievements and Hasheous matching (both depend on hashes). +- **Skip hashing on small hosts**: check **Skip hash calculation** on the Scan page, or set `filesystem.skip_hash_calculation: true` in `config.yml`. You lose RetroAchievements and Hasheous matching (both depend on hashes). - **Use SSD/NVMe for the library** if you care about hash performance. - **Raise `SEVEN_ZIP_TIMEOUT`** if you're scanning many large `.7z` archives. ## Scan seems to work but nothing shows up -Refresh the page — the ribbons on the home dashboard cache aggressively during a scan. Still blank? Check **Administration → Server Stats** — if counts are zero, the scan didn't actually persist anything. Usually a DB permission issue; check `docker logs romm 2>&1 | grep -i 'database'`. +Refresh the page; the ribbons on the home dashboard cache aggressively during a scan. Still blank? Check **Administration → Server Stats**; if counts are zero, the scan didn't actually persist anything. Usually a DB permission issue; check `docker logs romm 2>&1 | grep -i 'database'`. diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md index b469cd8f..2924d048 100644 --- a/docs/troubleshooting/sync.md +++ b/docs/troubleshooting/sync.md @@ -7,8 +7,8 @@ description: Diagnose push-pull sync, Client API Token pairing, and companion-ap Device Sync covers two distinct things: -- **API sync** (HTTPS, token-auth) — used by most companion apps. Argosy, Playnite, RommBrowser, mobile apps. -- **SSH sync** (push-pull task) — used by handhelds. Grout on muOS, DeckRommSync on Deck, etc. +- **API sync** (HTTPS, token-auth): used by most companion apps. Argosy, Playnite, RommBrowser, mobile apps. +- **SSH sync** (push-pull task): used by handhelds. Grout on muOS, DeckRommSync on Deck, etc. Troubleshooting paths differ. @@ -17,15 +17,15 @@ Troubleshooting paths differ. ### App says "token invalid" - **Token revoked.** Check Profile → Client API Tokens. Was it deleted? -- **Token expired.** Tokens can have optional expiry — check the expiry date on the token page. +- **Token expired.** Tokens can have optional expiry; check the expiry date on the token page. - **Scopes mismatch.** App needs scopes the token doesn't carry. Create a new token with the required scopes. See [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix). -- **User downgraded.** If the owning user got demoted (Admin → Viewer, say), tokens with scopes outside the new role stop working. +- **User downgraded.** If the owning user got demoted (Admin to Viewer, say), tokens with scopes outside the new role stop working. ### App can't reach RomM - **URL wrong.** Double-check what you entered in the app config. `https://romm.example.com`, not `https://romm.example.com/api` (the app appends the API path). - **TLS cert issues.** Self-signed certs cause problems. Use a proper cert (Let's Encrypt through your reverse proxy) or configure the app to skip cert validation (not recommended). -- **Firewall.** From the device, `curl https://romm.example.com/api/heartbeat` — should return JSON. If not, network path is blocked. +- **Firewall.** From the device, `curl https://romm.example.com/api/heartbeat`; should return JSON. If not, network path is blocked. ### Pairing code doesn't work @@ -51,7 +51,7 @@ Full flow: [Client API Tokens](../ecosystem/client-api-tokens.md). RomM's SSH key isn't authorised on the device. 1. Verify the **public** key (from `~/romm-sync-key.pub` on the host) is in the device's `~/.ssh/authorized_keys`. -2. Check line breaks — CRLF vs LF issues bite here. The `authorized_keys` file should have one key per line, Unix line endings. +2. Check line breaks (CRLF vs LF issues bite here). The `authorized_keys` file should have one key per line, Unix line endings. 3. Check file permissions on the device: `~/.ssh/` should be `700`, `~/.ssh/authorized_keys` should be `600`. See [SSH Sync → Configuring a device](../administration/ssh-sync.md#configuring-a-device). @@ -85,17 +85,17 @@ And verify the Docker bind mount isn't forcing different perms (ro is fine; mode ### Sync task doesn't run - **`SSH_PRIVATE_KEY_PATH` not set.** Check the RomM container's env. If the path doesn't exist inside the container, sync quietly no-ops. -- **No devices registered.** Administration → Devices — if empty, there's nothing to sync. +- **No devices registered.** Administration → Devices; if empty, there's nothing to sync. - **Cron expression wrong.** Default `PUSH_PULL_SYNC_INTERVAL_CRON=*/15 * * * *`. Invalid cron silently doesn't run. -- **Task failing silently.** `GET /api/tasks/status` — status shows per-task state. "failed" with a last-error is a direct pointer. +- **Task failing silently.** `GET /api/tasks/status`: status shows per-task state. "failed" with a last-error is a direct pointer. ## Play sessions Play sessions from a companion app (time-played tracking, Continue Playing ribbon): -- **Not appearing in RomM** — app isn't POSTing to `/api/play-sessions`. Check app's logs. -- **Duplicate sessions** — app is re-posting on sync. Known edge case for some companion apps; usually harmless. -- **Times look wrong** — timezone mismatch between device and RomM. RomM stores UTC; display converts to user TZ. +- **Not appearing in RomM**: app isn't POSTing to `/api/play-sessions`. Check app's logs. +- **Duplicate sessions**: app is re-posting on sync. Known edge case for some companion apps; usually harmless. +- **Times look wrong**: timezone mismatch between device and RomM. RomM stores UTC; display converts to user TZ. ## Devices panel says "offline" @@ -116,11 +116,11 @@ Admins can force a re-eval via Administration → Tasks → Refresh Smart Collec - **API sync**: `docker logs romm | grep -iE 'sync|device|token'`. - **SSH sync**: `docker logs romm | grep -iE 'push_pull|ssh'`. - Device-side logs from the companion app. Each app differs. -- [Discord](https://discord.gg/romm) `#help` — include the app name, its version, and the RomM log lines. +- [Discord](https://discord.gg/romm) `#help`: include the app name, its version, and the RomM log lines. ## See also -- [Client API Tokens](../ecosystem/client-api-tokens.md) — token + pairing flow reference. -- [Device Sync Protocol](../ecosystem/device-sync-protocol.md) — wire-level protocol details. -- [SSH Sync](../administration/ssh-sync.md) — server-side SSH sync config. -- [Companion apps](../ecosystem/community-apps.md) — list of integrations and their status. +- [Client API Tokens](../ecosystem/client-api-tokens.md): token + pairing flow reference. +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level protocol details. +- [SSH Sync](../administration/ssh-sync.md): server-side SSH sync config. +- [Companion apps](../ecosystem/community-apps.md): list of integrations and their status. diff --git a/docs/troubleshooting/synology.md b/docs/troubleshooting/synology.md index bee1f670..29681d8f 100644 --- a/docs/troubleshooting/synology.md +++ b/docs/troubleshooting/synology.md @@ -9,9 +9,9 @@ description: Fix DSM-specific permission and Docker issues. The usual Synology permission issue. Fix via SSH: -1. **Enable SSH** on the NAS if you haven't — [DSM guide](https://kb.synology.com/en-uk/DSM/tutorial/How_to_login_to_DSM_with_root_permission_via_SSH_Telnet). +1. **Enable SSH** on the NAS if you haven't; see the [DSM guide](https://kb.synology.com/en-uk/DSM/tutorial/How_to_login_to_DSM_with_root_permission_via_SSH_Telnet). 2. **SSH in** with your DSM admin account (same credentials as the DSM web UI). -3. **Find your UID/GID** — type `id` and note `uid=NNNN(user) gid=NNNN(group)`. +3. **Find your UID/GID**: type `id` and note `uid=NNNN(user) gid=NNNN(group)`. 4. **Fix permissions on every RomM host path**: ```sh @@ -40,11 +40,11 @@ Credit: the permission-mode string comes from [DrFrankenstein's Docker user guid Synology ships its own MariaDB on port `3306`. If you try to run RomM's MariaDB container on the same port, one of them won't bind. -Fix: map MariaDB to a different host port in your compose file (the Synology install guide uses `3309:3306`) — see [Synology install guide](../install/synology.md). +Fix: map MariaDB to a different host port in your compose file (the Synology install guide uses `3309:3306`); see [Synology install guide](../install/synology.md). ## RomM "Page not found" on first open -First-run takes a few minutes on a NAS — DB initialisation, migrations, static asset seeding. Tail the logs: +First-run takes a few minutes on a NAS: DB initialisation, migrations, static asset seeding. Tail the logs: ```sh sudo docker compose logs -f @@ -58,10 +58,10 @@ DSM occasionally rotates Docker's data directory or the BTRFS subvolume paths af Fix: -1. Don't panic — your host-mounted paths (`/volume1/...`) aren't touched. +1. Don't panic; your host-mounted paths (`/volume1/...`) aren't touched. 2. Check your compose file. Any volumes declared as *named* Docker volumes (as opposed to host bind mounts) might have been recreated empty. -3. Prefer host bind mounts on Synology specifically — `/volume1/docker/romm/resources` instead of a named `romm_resources` volume. The [Synology install guide](../install/synology.md) uses this pattern. +3. Prefer host bind mounts on Synology specifically: `/volume1/docker/romm/resources` instead of a named `romm_resources` volume. The [Synology install guide](../install/synology.md) uses this pattern. ## Still stuck -DSM + RomM issues are best asked in the [RomM Discord](https://discord.gg/romm) `#synology` channel — plenty of users there have seen the same issues and know the Synology quirks. +DSM + RomM issues are best asked in the [RomM Discord](https://discord.gg/romm) `#synology` channel. Plenty of users there have seen the same issues and know the Synology quirks. diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index 76f04f22..d52ff370 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -1,6 +1,6 @@ --- title: Account & Profile -description: Manage your RomM account — profile, password, avatar, API tokens, device pairing. +description: Manage your RomM account: profile, password, avatar, API tokens, device pairing. --- # Account & Profile @@ -15,14 +15,14 @@ Every user (Viewer, Editor, Admin) can manage their own profile. Admins can edit Editable from the profile page. Changing your email: -- **OIDC users** — email is usually governed by the IdP. RomM lets you change it here but OIDC login will re-match on the IdP-supplied email at next login. -- **Local users** — change freely. The new email is used for password reset (if email transport is configured — see [Authentication](../administration/authentication.md)). +- **OIDC users**: email is usually governed by the IdP. RomM lets you change it here but OIDC login will re-match on the IdP-supplied email at next login. +- **Local users**: change freely. The new email is used for password reset (if email transport is configured; see [Authentication](../administration/authentication.md)). ### Password -For local accounts: **Current password** + **New password** (twice). RomM uses bcrypt — no plaintext storage, no password recovery by the admin (admins reset by setting a new password; they can't see your old one). +For local accounts: **Current password** + **New password** (twice). RomM uses bcrypt: no plaintext storage, no password recovery by the admin (admins reset by setting a new password; they can't see your old one). -OIDC users don't have local passwords — authentication is via the IdP. Password fields are hidden on the profile page. +OIDC users don't have local passwords; authentication is via the IdP. Password fields are hidden on the profile page. ### Avatar @@ -30,23 +30,23 @@ Upload a PNG / JPEG / WebP. Displayed next to your name across the UI. ### Preferred username in OIDC -If you're an OIDC user and want RomM to show your `preferred_username` from the token instead of your email local-part, the operator can set `OIDC_USERNAME_ATTRIBUTE=preferred_username` — see [OIDC Setup](../administration/oidc/index.md). +If you're an OIDC user and want RomM to show your `preferred_username` from the token instead of your email local-part, the operator can set `OIDC_USERNAME_ATTRIBUTE=preferred_username`. See [OIDC Setup](../administration/oidc/index.md). ## Client API tokens Long-lived API tokens for companion apps, scripts, and integrations. Each token is scoped to a subset of your user's scopes, and optionally expires. -See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md) — this section is the "how I create one from the UI" version. +See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md); this section is the "how I create one from the UI" version. ### Creating a token 1. **Profile → Client API Tokens → + New Token**. 2. Pick: - - **Name** — descriptive (e.g. "Grout on my RG35XX"). - - **Scopes** — which permissions to include. Default is read-only; tick write scopes deliberately. - - **Expiry** — optional. Blank = never expires. + - **Name**: descriptive (e.g. "Grout on my RG35XX"). + - **Scopes**: which permissions to include. Default is read-only; tick write scopes deliberately. + - **Expiry**: optional. Blank = never expires. 3. **Create**. -4. The token appears **once**. Copy it immediately — you can't retrieve it later. +4. The token appears **once**. Copy it immediately; you can't retrieve it later. Token format: `rmm_` + 40 hex chars. Treat it like a password. @@ -54,18 +54,19 @@ Token format: `rmm_` + 40 hex chars. Treat it like a password. For handheld apps like Grout, typing a 44-character token on a thumbstick isn't realistic. Instead: + 1. **Profile → Client API Tokens → [token] → Pair Device**. 2. A short numeric code appears (8 digits, valid for 5 minutes). 3. On the device (Grout app, etc.): enter the code. 4. The device exchanges the code for the full token via the pairing API. -5. Done — token is on the device, you never typed it. +5. Done: token is on the device, you never typed it. Full flow in [Client API Tokens](../ecosystem/client-api-tokens.md). ### Limits - **25 active tokens per user.** Delete old ones to free slots. -- **Tokens carry a subset of your scopes.** If an admin demotes you to Viewer, any token you hold with admin scopes stops working — tokens don't escalate privileges beyond the owning user's current role. +- **Tokens carry a subset of your scopes.** If an admin demotes you to Viewer, any token you hold with admin scopes stops working; tokens don't escalate privileges beyond the owning user's current role. ### Revoking @@ -100,7 +101,7 @@ Returns your user record plus a `personal_data` array of per-ROM ratings, notes, ## Deleting your account -Self-deletion isn't available in 5.0. Ask an admin to delete your account — they can do it from **Administration → Users → [your account] → Delete**. +Self-deletion isn't available in 5.0. Ask an admin to delete your account; they can do it from **Administration → Users → [your account] → Delete**. When deleted: @@ -115,14 +116,14 @@ When deleted: If you signed in via OIDC: - Most fields are still editable on the RomM side. Email and role may be overwritten on next login based on IdP claims. -- **Logging out of RomM doesn't log out of the IdP** unless the operator has enabled `OIDC_RP_INITIATED_LOGOUT` — see [OIDC Setup](../administration/oidc/index.md). +- **Logging out of RomM doesn't log out of the IdP** unless the operator has enabled `OIDC_RP_INITIATED_LOGOUT`. See [OIDC Setup](../administration/oidc/index.md). - Unlinking OIDC isn't currently supported from the profile page. If you want to convert to a local account, ask an admin to delete your OIDC-linked account, then re-register with a local email+password. ## Language and UI preferences -Lives on a separate page — **Profile → User Interface** — not here. Covers: +Lives on a separate page (**Profile → User Interface**), not here. Covers: -- [Language](languages.md) — 19 locales. +- [Language](languages.md): 19 locales. - Theme (Dark / Light / Auto). - Home dashboard ribbons. - Collection types (toggle [virtual collections](virtual-collections.md) dimensions). @@ -133,7 +134,7 @@ See those pages for detail. ## Troubleshooting -- **"Current password incorrect"** — caps lock, or the password was changed by an admin. Ask them. -- **Avatar upload silently fails** — file is too large; RomM accepts up to a few MB. Resize. -- **Can't create API token** — you've hit the 25-token cap. Revoke old ones. -- **OIDC login came back as Viewer when I'm an Admin** — role claim mapping is misconfigured or missing. See [OIDC Troubleshooting → Role mapping](../troubleshooting/authentication.md#user-is-created-but-stays-viewer-even-though-they-should-be-admin). +- **"Current password incorrect"**: caps lock, or the password was changed by an admin. Ask them. +- **Avatar upload silently fails**: file is too large; RomM accepts up to a few MB. Resize. +- **Can't create API token**: you've hit the 25-token cap. Revoke old ones. +- **OIDC login came back as Viewer when I'm an Admin**: role claim mapping is misconfigured or missing. See [OIDC Troubleshooting → Role mapping](../troubleshooting/authentication.md#user-is-created-but-stays-viewer-even-though-they-should-be-admin). diff --git a/docs/using/collections.md b/docs/using/collections.md index 80929685..e343e09b 100644 --- a/docs/using/collections.md +++ b/docs/using/collections.md @@ -1,6 +1,6 @@ --- title: Collections -description: Hand-curated ROM collections — create, edit, share, and delete. +description: Hand-curated ROM collections, create, edit, share, and delete. --- # Collections @@ -9,8 +9,8 @@ A **collection** is a named group of ROMs you curate by hand. Add or remove game RomM has three collection types. This page covers **standard** collections. For the other two: -- [Smart Collections](smart-collections.md) — rule-based, auto-populating. -- [Virtual Collections](virtual-collections.md) — auto-generated by RomM (by genre, developer, year, tags). +- [Smart Collections](smart-collections.md): rule-based, auto-populating. +- [Virtual Collections](virtual-collections.md): auto-generated by RomM (by genre, developer, year, tags). ## Creating a collection @@ -21,15 +21,15 @@ Two ways: 1. Click **Collections** in the menu bar → **+ New Collection**. 2. Pick a name, optional description, optional cover image. 3. Set **visibility**: - - **Private** — only you see it. - - **Public** — any signed-in user on this instance sees it. Needs `collections.write` scope to toggle (Editor or Admin). + - **Private**: only you see it. + - **Public**: any signed-in user on this instance sees it. Needs `collections.write` scope to toggle (Editor or Admin). 4. Save. The collection is created empty. ### From a search result 1. Run a search or apply filters in the library. 2. Click the **New Collection from results** icon next to the search bar. -3. Name it and save — RomM populates it with the current result set. +3. Name it and save; RomM populates it with the current result set. This is the fastest way to bootstrap a collection from "all the PSX games tagged (USA)" or similar. @@ -39,7 +39,7 @@ Hover a game card → context menu (…) → **Add to Collection** → pick the Or from the game detail page → Context menu → **Collections** → toggle. -Bulk add is not available in 5.0 for standard collections — use [Smart Collections](smart-collections.md) if you want rule-based population. +Bulk add is not available in 5.0 for standard collections; use [Smart Collections](smart-collections.md) if you want rule-based population. ## Removing ROMs @@ -60,15 +60,15 @@ Change: ## Sharing -If the collection is **Public**, any other user on the same RomM instance sees it in their Collections drawer. They can browse, play, download — but can't add or remove ROMs. Only the owner edits. +If the collection is **Public**, any other user on the same RomM instance sees it in their Collections drawer. They can browse, play, download, but can't add or remove ROMs. Only the owner edits. -For cross-instance sharing, generate a link via the collection drawer → **Copy link** — anyone with access to your RomM instance and the link can open it directly. +For cross-instance sharing, generate a link via the collection drawer → **Copy link**. Anyone with access to your RomM instance and the link can open it directly. ## Deleting Collection drawer → **Delete** → confirm. -Deletion only removes the collection itself. The ROMs inside it stay in the library — nothing touches the actual files. +Deletion only removes the collection itself. The ROMs inside it stay in the library; nothing touches the actual files. ## Who can do what @@ -77,16 +77,16 @@ Deletion only removes the collection itself. The ROMs inside it stay in the libr | See public collections | ✓ | ✓ | ✓ | | Create own private collection | ✓ | ✓ | ✓ | | Edit own collection | ✓ | ✓ | ✓ | -| Make a collection public | — | ✓ | ✓ | -| See all users' collections (admin panel) | — | — | ✓ | +| Make a collection public | - | ✓ | ✓ | +| See all users' collections (admin panel) | - | - | ✓ | Scope mapping and the full permission matrix in [Users & Roles](../administration/users-and-roles.md#scope-matrix). ## Tips -- **Don't make a collection per genre** — you want [Virtual Collections](virtual-collections.md) for that, auto-populated. -- **Don't make a collection for "Backlogged games"** — that's what the Personal → Status flag is for, and it's filterable without a collection. -- **Do make a collection for curated playthroughs** — "RomM Staff Picks", "Games I Want to Stream", "Favourites for My Kid", "Halloween Games". Hand-picked context is what collections are for. +- **Don't make a collection per genre**: you want [Virtual Collections](virtual-collections.md) for that, auto-populated. +- **Don't make a collection for "Backlogged games"**: that's what the Personal → Status flag is for, and it's filterable without a collection. +- **Do make a collection for curated playthroughs**: "RomM Staff Picks", "Games I Want to Stream", "Favourites for My Kid", "Halloween Games". Hand-picked context is what collections are for. ## API diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md index ac3ed13b..09db4218 100644 --- a/docs/using/console-mode.md +++ b/docs/using/console-mode.md @@ -7,7 +7,7 @@ description: TV + gamepad UI for RomM. Spatial navigation, SFX, focus sounds, no **Console Mode** is a second UI for RomM, living at `/console`, designed for TV screens and gamepad input. Same instance, same data, completely different UX. -New in 5.0. If you're on a desktop with a keyboard and mouse, you probably don't need it — the main [Library](library.md) view is your home. But if you're on: +New in 5.0. If you're on a desktop with a keyboard and mouse, you probably don't need it; the main [Library](library.md) view is your home. But if you're on: - A TV with RomM on a stick / Shield / Pi / mini-PC, - A handheld running muOS / Knulli / Batocera / Steam Deck, @@ -21,7 +21,7 @@ Three ways: 1. **Menu bar → Console icon** (looks like a small controller). 2. Type `/console` after your RomM URL: `https://romm.example.com/console`. -3. Set the default view to Console Mode in **Profile → User Interface → Default view** — signs you in straight into `/console` every time. +3. Set the default view to Console Mode in **Profile → User Interface → Default view**; this signs you in straight into `/console` every time. Bookmarking `/console` on your TV browser is the most common pattern. @@ -29,7 +29,7 @@ Bookmarking `/console` on your TV browser is the most common pattern. ### Gamepad -Standard gamepad API. Xbox, PS, 8BitDo, Steam Controller, Switch Pro — anything the browser sees as a standard gamepad works. Handhelds with native gamepad input (Steam Deck, muOS devices) work out of the box. +Standard gamepad API. Xbox, PS, 8BitDo, Steam Controller, Switch Pro: anything the browser sees as a standard gamepad works. Handhelds with native gamepad input (Steam Deck, muOS devices) work out of the box. Default bindings (most platforms, configurable): @@ -45,7 +45,7 @@ Default bindings (most platforms, configurable): | Start | Main menu | | Select / Back | Filters | -Button labels follow the Xbox convention regardless of actual controller — "A" is always the bottom face button. +Button labels follow the Xbox convention regardless of actual controller: "A" is always the bottom face button. ### Keyboard (fallback) @@ -61,13 +61,13 @@ If you're browsing `/console` without a gamepad for some reason: ### Touch -Console Mode is gamepad-first, but touch works on handhelds without native gamepad input — tap to activate, swipe to scroll. +Console Mode is gamepad-first, but touch works on handhelds without native gamepad input: tap to activate, swipe to scroll. ## What's different from the main UI ### Spatial navigation -Every focusable element is placed on a 2D grid. D-pad Up / Down / Left / Right moves to the visually-nearest focusable element — not the next DOM element. Works the way a TV UI should. +Every focusable element is placed on a 2D grid. D-pad Up / Down / Left / Right moves to the visually-nearest focusable element, not the next DOM element. Works the way a TV UI should. ### Focus sounds @@ -77,6 +77,7 @@ Subtle SFX play on focus, activate, and back. Turn off in **Settings → Console Cards and buttons are sized for 10-foot viewing. Grids show fewer items per row than the main UI. + ### Simpler information density The game detail view uses full-screen tabs instead of side-by-side panels. Filter panels are dedicated screens rather than popovers. @@ -87,7 +88,7 @@ The cursor disappears after a few seconds of no movement. Wiggle the mouse (or t ### Theme -Console Mode uses its own color palette — higher contrast, bigger typography. It follows the main UI's dark/light mode setting but with TV-appropriate rendering. +Console Mode uses its own color palette: higher contrast, bigger typography. It follows the main UI's dark/light mode setting but with TV-appropriate rendering. ## Features @@ -96,7 +97,7 @@ Most of the main UI's features are available in Console Mode: - ✓ Browse library, platforms, collections. - ✓ Search and filters. - ✓ Play (launches [In-Browser Play](in-browser-play.md) or shows a download prompt if the platform isn't browser-playable). -- ✓ Saves and states — upload, select, delete. +- ✓ Saves and states: upload, select, delete. - ✓ Smart and virtual collections. - ✓ RetroAchievements progression display. @@ -123,7 +124,7 @@ Not yet in Console Mode (still use the main UI): ### Background art -**Settings → Console Mode → Backgrounds.** Use the focused game's screenshot as a faded background — makes Console Mode feel like a proper console UI. Off by default. +**Settings → Console Mode → Backgrounds.** Use the focused game's screenshot as a faded background; makes Console Mode feel like a proper console UI. Off by default. ## Launching games @@ -135,28 +136,28 @@ If not, Console Mode shows a **Download** prompt with a QR code for mobile shari ### Pre-launch disc/save/state picker -For multi-disc games, Console Mode asks which disc to boot before launching. For games with existing saves/states, you can pick which to resume from. No dialogs in the middle of a session — everything's picked up front. +For multi-disc games, Console Mode asks which disc to boot before launching. For games with existing saves/states, you can pick which to resume from. No dialogs in the middle of a session; everything's picked up front. ## Known limitations -- **Admin features aren't available** — if you're the admin, flip back to the main UI for scans / user management. -- **Some metadata tabs collapse** — the main UI's "Related Games" + "Additional Content" tabs may be merged on the smaller Console detail view. -- **Mobile browsers with no gamepad** — touch works, but the UX is designed for gamepads, not fingers. Use the main UI or the Argosy mobile app ([Ecosystem](../ecosystem/argosy.md)). +- **Admin features aren't available**: if you're the admin, flip back to the main UI for scans / user management. +- **Some metadata tabs collapse**: the main UI's "Related Games" + "Additional Content" tabs may be merged on the smaller Console detail view. +- **Mobile browsers with no gamepad**: touch works, but the UX is designed for gamepads, not fingers. Use the main UI or the Argosy mobile app ([Ecosystem](../ecosystem/argosy.md)). ## Handheld-specific notes Running on muOS / Batocera / Knulli / a Steam Deck? Consider: -- **[Grout](../ecosystem/grout.md)** — official handheld companion that syncs saves/states to/from the device. -- **[Argosy Launcher](../ecosystem/argosy.md)** — Android handhelds that can run a browser, but want a native-feeling app. +- **[Grout](../ecosystem/grout.md)**: official handheld companion that syncs saves/states to/from the device. +- **[Argosy Launcher](../ecosystem/argosy.md)**: Android handhelds that can run a browser, but want a native-feeling app. Both use [Client API Tokens](../ecosystem/client-api-tokens.md) for auth. ## Troubleshooting -- **Gamepad not detected** — Chrome sometimes needs a button press on the page before enumerating gamepads. Press any button and it'll show up. -- **Cursor stays visible** — you have a USB mouse plugged into a handheld. Unplug it or set **Cursor hide** to "always" in Console settings. -- **Laggy navigation** — low-powered device running a heavy browser. Try Firefox or a lighter browser build. -- **SFX plays during video** — turn it off in **Console → Audio** or lower volume. +- **Gamepad not detected**: Chrome sometimes needs a button press on the page before enumerating gamepads. Press any button and it'll show up. +- **Cursor stays visible**: you have a USB mouse plugged into a handheld. Unplug it or set **Cursor hide** to "always" in Console settings. +- **Laggy navigation**: low-powered device running a heavy browser. Try Firefox or a lighter browser build. +- **SFX plays during video**: turn it off in **Console → Audio** or lower volume. More in [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/using/downloads.md b/docs/using/downloads.md index 8efc4039..381c3717 100644 --- a/docs/using/downloads.md +++ b/docs/using/downloads.md @@ -1,6 +1,6 @@ --- title: Downloads -description: Download ROMs from RomM — single, bulk, QR codes, copy-link, and streaming for third-party apps. +description: Download ROMs from RomM: single, bulk, QR codes, copy-link, and streaming for third-party apps. --- # Downloads @@ -9,23 +9,23 @@ description: Download ROMs from RomM — single, bulk, QR codes, copy-link, and The quick path: hover a game card → click **Download**. Or from the game detail page → **Download** button. -RomM streams the file directly — no temp file on disk, no copy, no waiting for packaging. Large ROMs and multi-disc sets download just as quickly as small ones. +RomM streams the file directly: no temp file on disk, no copy, no waiting for packaging. Large ROMs and multi-disc sets download just as quickly as small ones. -For multi-file games (folder-based), RomM streams a zip on the fly — see [Multi-file downloads](#multi-file-and-bulk-downloads-nginx-mod_zip) below. +For multi-file games (folder-based), RomM streams a zip on the fly. See [Multi-file downloads](#multi-file-and-bulk-downloads-nginx-mod_zip) below. ## Copy download link -For cases where you want the URL, not the file right now — sending it to another device, pasting into a script, using an external download manager. +For cases where you want the URL, not the file right now: sending it to another device, pasting into a script, using an external download manager. -Context menu (…) on a game card → **Copy download link** → URL is on your clipboard. +Context menu (…) on a game card → **Copy download link**, and the URL is on your clipboard. -Anyone with access to the link and the server can download. By default the link requires your session cookie or a bearer token — see [Third-party download auth](#third-party-download-auth) for the exception. +Anyone with access to the link and the server can download. By default the link requires your session cookie or a bearer token; see [Third-party download auth](#third-party-download-auth) for the exception. ## QR code For handheld-to-desktop or desktop-to-phone transfers without typing a URL. -Context menu (…) → **Show QR Code** → point the other device's camera at the screen. +Context menu (…) → **Show QR Code**, then point the other device's camera at the screen. The QR decodes to the same URL as Copy Link. Same auth rules apply. @@ -35,17 +35,17 @@ RomM's nginx is built with `mod_zip`, which streams a zip archive over HTTP with ### Multi-disc / multi-file games -When a game is stored as a folder (multi-disc, game + DLC, game + patch, etc.), clicking **Download** builds a zip on the fly containing the whole folder. The browser sees a zip download start immediately — no "packaging…" delay. +When a game is stored as a folder (multi-disc, game + DLC, game + patch, etc.), clicking **Download** builds a zip on the fly containing the whole folder. The browser sees a zip download start immediately, no "packaging…" delay. ### Bulk download from a gallery -Multi-select ROMs in any gallery (platform view, collection, search results) → toolbar → **Download selected** → single zip with every selected ROM preserved in its platform folder structure. +Multi-select ROMs in any gallery (platform view, collection, search results) → toolbar → **Download selected**, producing a single zip with every selected ROM preserved in its platform folder structure. -No practical limit — the zip is streamed, so memory doesn't grow with selection size. Disk I/O and network bandwidth are the actual limits. +No practical limit: the zip is streamed, so memory doesn't grow with selection size. Disk I/O and network bandwidth are the actual limits. ## Third-party download auth -Some third-party tools (a dumb emulator loading a ROM by URL, a browser extension, a homebrew Switch app) can't send a bearer token. For those, admins can turn off download-endpoint auth: +Some third-party tools (a dumb emulator loading a ROM by URL, a browser extension, a homebrew Switch app) can't send a bearer token. For those, admins can turn off download-endpoint auth. ```yaml environment: @@ -67,17 +67,17 @@ curl -H "Authorization: Bearer rmm_..." \ ## Streaming to an emulator -Some emulators can take an HTTP URL directly — point them at the same URL the Copy Link button produces. With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` and a reverse proxy that restricts access, you can set up truly remote ROM loading from a handheld over Wi-Fi. +Some emulators can take an HTTP URL directly; point them at the same URL the Copy Link button produces. With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` and a reverse proxy that restricts access, you can set up truly remote ROM loading from a handheld over Wi-Fi. ## Download history -Not tracked in 5.0. RomM doesn't log downloads for privacy reasons — use your reverse proxy's access log if you need to audit. +Not tracked in 5.0. RomM doesn't log downloads for privacy reasons; use your reverse proxy's access log if you need to audit. ## Troubleshooting -- **Download stalls at N%** — usually the reverse proxy buffering to disk. See [Reverse Proxy → Nginx Proxy Manager](../install/reverse-proxy.md#nginx-proxy-manager) for the `proxy_max_temp_file_size 0` fix. -- **Multi-file zip download is corrupt** — disk may have filled up during streaming, or the nginx mod_zip build is broken. Check `docker logs romm | grep mod_zip`. -- **Bulk download ends early** — reverse proxy is enforcing a request timeout. Raise `proxy_read_timeout` — see [Kubernetes Troubleshooting](../troubleshooting/kubernetes.md#websockets-disconnect-immediately) for nginx-ingress annotation pattern. +- **Download stalls at N%**: usually the reverse proxy buffering to disk. See [Reverse Proxy → Nginx Proxy Manager](../install/reverse-proxy.md#nginx-proxy-manager) for the `proxy_max_temp_file_size 0` fix. +- **Multi-file zip download is corrupt**: disk may have filled up during streaming, or the nginx mod_zip build is broken. Check `docker logs romm | grep mod_zip`. +- **Bulk download ends early**: reverse proxy is enforcing a request timeout. Raise `proxy_read_timeout`; see [Kubernetes Troubleshooting](../troubleshooting/kubernetes.md#websockets-disconnect-immediately) for nginx-ingress annotation pattern. ## API diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md index 83d5778e..f58d0b2d 100644 --- a/docs/using/in-browser-play.md +++ b/docs/using/in-browser-play.md @@ -1,20 +1,20 @@ --- title: In-Browser Play -description: Play games directly in your browser with EmulatorJS and Ruffle — controls, saves, netplay, fullscreen. +description: Play games directly in your browser with EmulatorJS and Ruffle: controls, saves, netplay, fullscreen. --- # In-Browser Play RomM ships with two in-browser emulators: -- **EmulatorJS** — RetroArch cores compiled to WebAssembly. Covers NES, SNES, Genesis, N64, PSX, PSP, Saturn, and 30+ other platforms. -- **Ruffle** — Flash / Shockwave emulator for browser games. +- **EmulatorJS**: RetroArch cores compiled to WebAssembly. Covers NES, SNES, Genesis, N64, PSX, PSP, Saturn, and 30+ other platforms. +- **Ruffle**: Flash / Shockwave emulator for browser games. -Hit the **Play** button on any supported game — the emulator loads full-screen in a new page. Same UI on desktop, mobile, and Console Mode. +Hit the **Play** button on any supported game; the emulator loads full-screen in a new page. Same UI on desktop, mobile, and Console Mode. ## EmulatorJS -[EmulatorJS](https://emulatorjs.org/) is a web-based emulator running [RetroArch](https://www.retroarch.com) via [Emscripten](https://emscripten.org/) — so cores you know from RetroArch show up here. +[EmulatorJS](https://emulatorjs.org/) is a web-based emulator running [RetroArch](https://www.retroarch.com) via [Emscripten](https://emscripten.org/), so cores you know from RetroArch show up here. !!! warning "Emulation is resource-intensive" Older or less-powerful devices may struggle, especially with demanding cores (Dreamcast, Saturn, PSP). Try a different browser or device before filing a bug. @@ -29,7 +29,7 @@ Hit the **Play** button on any supported game — the emulator loads full-screen | 3DO | `opera` | | Amiga | `puae` | | Arcade / MAME | `mame2003_plus`, `mame2003`, `fbneo` | -| Atari 2600 / 5200 / 7800 / Jaguar / Lynx | Various — `stella2014`, `atari800`, `prosystem`, `virtualjaguar`, `handy` | +| Atari 2600 / 5200 / 7800 / Jaguar / Lynx | Various: `stella2014`, `atari800`, `prosystem`, `virtualjaguar`, `handy` | | Commodore 64 | `vice_x64sc` | | ColecoVision | `gearcoleco` | | DOOM | `prboom` | @@ -53,7 +53,7 @@ Full up-to-date list: [EmulatorJS Systems docs](https://emulatorjs.org/docs/syst ### Firmware -Some platforms need BIOS / firmware — PlayStation, Saturn, Sega CD, PSP, DS. Upload via [Firmware Management](../administration/firmware-management.md) or by dropping files in the right `bios/` folder and rescanning. +Some platforms need BIOS / firmware: PlayStation, Saturn, Sega CD, PSP, DS. Upload via [Firmware Management](../administration/firmware-management.md) or by dropping files in the right `bios/` folder and rescanning. !!! note "Zip bundles for multi-file firmware" Some cores need several BIOS files. Create a **zip archive** containing all of them and upload that zip through the Firmware tab on the platform detail page. EmulatorJS picks it up as a single firmware bundle. See [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/) for the per-platform file list. @@ -72,23 +72,23 @@ If the platform doesn't support in-browser play, Play is replaced with Download. EmulatorJS maps keyboard and gamepad automatically. Defaults are core-specific, but approximate the original console layout. Rebind in-game via the **Menu** button during play. -Operator-level default overrides live in `config.yml` — see [Configuration File → `emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols). +Operator-level default overrides live in `config.yml`; see [Configuration File → `emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols). -Keyboard + gamepad can be used simultaneously for multi-player — player 2 on the gamepad, player 1 on the keyboard, for example. +Keyboard + gamepad can be used simultaneously for multi-player: player 2 on the gamepad, player 1 on the keyboard, for example. ### Saves and states RomM integrates with EmulatorJS so save-files and save-states are loaded and saved automatically: -- **Before launch** — if multiple saves exist, RomM asks which to load. -- **During play** — any in-emulator save (SRAM flush) or state creation is written straight back to RomM's storage. -- **After quit** — everything is already persisted. No manual download. +- **Before launch**: if multiple saves exist, RomM asks which to load. +- **During play**: any in-emulator save (SRAM flush) or state creation is written straight back to RomM's storage. +- **After quit**: everything is already persisted. No manual download. Full details: [Saves & States](saves-and-states.md). ### Cheats -In-game Menu → **Cheats** → add manually or load a list. Saved cheats persist per-user per-ROM. RomM doesn't ship a cheat database — you bring your own codes. +In-game Menu → **Cheats** → add manually or load a list. Saved cheats persist per-user per-ROM. RomM doesn't ship a cheat database; you bring your own codes. ### Screenshots @@ -96,12 +96,12 @@ During play: in-game Menu → **Screenshot**. Saved to your personal screenshot ### Fullscreen -- **F11** — browser fullscreen. -- In-game Menu → **Fullscreen** button — same thing. +- **F11**: browser fullscreen. +- In-game Menu → **Fullscreen** button: same thing. ### Multi-disc games -RomM asks which disc to boot before launching. During play, in-game Menu → **Switch Disc** → pick another disc without exiting (works for cores that support runtime disc swap — PSX, Saturn). +RomM asks which disc to boot before launching. During play, in-game Menu → **Switch Disc** → pick another disc without exiting (works for cores that support runtime disc swap: PSX, Saturn). ### Cache / unload behaviour @@ -109,11 +109,11 @@ Operators can tune how aggressively the emulator caches and unloads via [`emulat ### Full-screen on play -Profile → User Interface → **Fullscreen on launch** — always enters fullscreen when you hit Play. Handy on TVs. +Profile → User Interface → **Fullscreen on launch**: always enters fullscreen when you hit Play. Handy on TVs. ### Per-core settings -In-game Menu → **Settings** — core-specific knobs (vsync, region, sound quality). Your tweaks persist per-core per-user, but operator defaults in [`config.yml`](../reference/configuration-file.md#emulatorjssettings) set the initial values. +In-game Menu → **Settings**: core-specific knobs (vsync, region, sound quality). Your tweaks persist per-core per-user, but operator defaults in [`config.yml`](../reference/configuration-file.md#emulatorjssettings) set the initial values. ### Netplay @@ -128,7 +128,7 @@ See [Netplay](netplay.md). One-page deep dive on hosting/joining, ICE servers, a ### Controls -Flash games were typically designed for mouse + keyboard — Ruffle passes input through as-is. No controller mapping; gamepad-only users will struggle with most Flash titles. +Flash games were typically designed for mouse + keyboard; Ruffle passes input through as-is. No controller mapping; gamepad-only users will struggle with most Flash titles. ### Saves @@ -136,7 +136,7 @@ Ruffle writes Flash's local-storage to RomM's assets directory. Appears under th ### Supported games -Most 2D Flash games work. 3D Shockwave and some advanced ActionScript titles may have rendering glitches — see [Ruffle compatibility](https://ruffle.rs/#compatibility). +Most 2D Flash games work. 3D Shockwave and some advanced ActionScript titles may have rendering glitches; see [Ruffle compatibility](https://ruffle.rs/#compatibility). ### Metadata @@ -144,10 +144,10 @@ If you enable the [Flashpoint](../administration/metadata-providers.md#flashpoin ## Troubleshooting -- **Game won't boot** — check firmware is uploaded for platforms that need it. `docker logs romm | grep -i emulator` for server-side hints. -- **Black screen, no audio** — core is incompatible with your browser. Try a different browser (Chrome / Firefox are most tested) or a different core via in-game Menu → **Core**. -- **Controls do nothing** — browser needs focus; click once on the emulator canvas. Some cores need a button press to enumerate gamepads. -- **Netplay "failed to start game"** — see [Netplay troubleshooting](../troubleshooting/netplay.md). -- **DOS game fails to autorun** — try [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjs) in `config.yml`. +- **Game won't boot**: check firmware is uploaded for platforms that need it. `docker logs romm | grep -i emulator` for server-side hints. +- **Black screen, no audio**: core is incompatible with your browser. Try a different browser (Chrome / Firefox are most tested) or a different core via in-game Menu → **Core**. +- **Controls do nothing**: browser needs focus; click once on the emulator canvas. Some cores need a button press to enumerate gamepads. +- **Netplay "failed to start game"**: see [Netplay troubleshooting](../troubleshooting/netplay.md). +- **DOS game fails to autorun**: try [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjs) in `config.yml`. More in [In-Browser Play Troubleshooting](../troubleshooting/in-browser-play.md). diff --git a/docs/using/index.md b/docs/using/index.md index dd632c46..05342bd8 100644 --- a/docs/using/index.md +++ b/docs/using/index.md @@ -1,48 +1,48 @@ --- title: Using RomM -description: End-user guide — how to browse, play, collect, patch, and share from the RomM UI. +description: End-user guide on how to browse, play, collect, patch, and share from the RomM UI. --- # Using RomM -You've got an account, you're signed in. This section covers everything you do as a user — not an admin. +You've got an account, you're signed in. This section covers everything you do as a user, not an admin. Admins: see [Administration](../administration/index.md) for operator tasks. ## Browse and play -- **[Library](library.md)** — the main UI: home dashboard, platform and game cards, filters, the search bar. -- **[In-Browser Play](in-browser-play.md)** — EmulatorJS for retro consoles, Ruffle for Flash. Save states, cheats, fullscreen. -- **[Downloads](downloads.md)** — single, bulk, QR code, copy link. -- **[Uploads](uploads.md)** — drag-and-drop ROMs, firmware, saves, states, screenshots. +- **[Library](library.md)**: the main UI: home dashboard, platform and game cards, filters, the search bar. +- **[In-Browser Play](in-browser-play.md)**: EmulatorJS for retro consoles, Ruffle for Flash. Save states, cheats, fullscreen. +- **[Downloads](downloads.md)**: single, bulk, QR code, copy link. +- **[Uploads](uploads.md)**: drag-and-drop ROMs, firmware, saves, states, screenshots. ## Organise -- **[Collections](collections.md)** — hand-curated lists. -- **[Smart Collections](smart-collections.md)** — rule-based, auto-populating. -- **[Virtual Collections](virtual-collections.md)** — auto-generated by genre, developer, year, tags. +- **[Collections](collections.md)**: hand-curated lists. +- **[Smart Collections](smart-collections.md)**: rule-based, auto-populating. +- **[Virtual Collections](virtual-collections.md)**: auto-generated by genre, developer, year, tags. ## Personal data -- **[Saves & States](saves-and-states.md)** — upload, sync, select before launching. -- **[RetroAchievements](retroachievements.md)** — link your RA account, surface progression. -- **[Account & Profile](account-and-profile.md)** — change your password, email, avatar. API tokens. +- **[Saves & States](saves-and-states.md)**: upload, sync, select before launching. +- **[RetroAchievements](retroachievements.md)**: link your RA account, surface progression. +- **[Account & Profile](account-and-profile.md)**: change your password, email, avatar. API tokens. ## Special modes -- **[Console Mode](console-mode.md)** — `/console` UI for TVs and gamepads. Spatial navigation, SFX, no mouse needed. -- **[Install as PWA](pwa.md)** — add RomM to your phone/desktop home screen. -- **[Mobile & TV](mobile-and-tv.md)** — device-specific usage patterns. +- **[Console Mode](console-mode.md)**: `/console` UI for TVs and gamepads. Spatial navigation, SFX, no mouse needed. +- **[Install as PWA](pwa.md)**: add RomM to your phone/desktop home screen. +- **[Mobile & TV](mobile-and-tv.md)**: device-specific usage patterns. ## Tools -- **[ROM Patcher](rom-patcher.md)** — apply IPS/UPS/BPS/PPF and more in-browser. -- **[Netplay](netplay.md)** — EmulatorJS multiplayer sessions. +- **[ROM Patcher](rom-patcher.md)**: apply IPS/UPS/BPS/PPF and more in-browser. +- **[Netplay](netplay.md)**: EmulatorJS multiplayer sessions. ## Settings -- **[Languages](languages.md)** — 19 locales. +- **[Languages](languages.md)**: 19 locales. --- -Looking for something specific? Every page in this section is linked from the dashboard or the profile drawer. If you can't find a feature, check [Core Concepts](../getting-started/concepts.md) — the vocabulary index. +Looking for something specific? Every page in this section is linked from the dashboard or the profile drawer. If you can't find a feature, check [Core Concepts](../getting-started/concepts.md), the vocabulary index. diff --git a/docs/using/languages.md b/docs/using/languages.md index b271ac21..8256d7a7 100644 --- a/docs/using/languages.md +++ b/docs/using/languages.md @@ -1,17 +1,17 @@ --- title: Languages -description: Switch the RomM UI language — 19 locales supported in 5.0. +description: Switch the RomM UI language, 19 locales supported in 5.0. --- # Languages -RomM's UI is translated into 19 locales. Pick yours from **Profile → User Interface → Language**. The change is immediate — no reload, no re-login. +RomM's UI is translated into 19 locales. Pick yours from **Profile → User Interface → Language**. The change is immediate: no reload, no re-login. ## Supported locales | Code | Language | | --- | --- | -| `en_US` | English (United States) — default | +| `en_US` | English (United States), default | | `en_GB` | English (United Kingdom) | | `es_ES` | Spanish (Spain) | | `fr_FR` | French | @@ -29,7 +29,7 @@ RomM's UI is translated into 19 locales. Pick yours from **Profile → User Inte | `zh_CN` | Chinese (Simplified) | | `zh_TW` | Chinese (Traditional) | -Two more ship as work-in-progress with partial coverage — check the language dropdown to see the current list. +Two more ship as work-in-progress with partial coverage; check the language dropdown to see the current list. ## What gets translated @@ -39,15 +39,15 @@ Two more ship as work-in-progress with partial coverage — check the language d Not translated: -- **Game metadata** — titles, descriptions, genres come from metadata providers. English is usually the source language; IGDB has some localisation but coverage is uneven. -- **Your own content** — collection names, notes, user-uploaded media. -- **Docs site** (what you're reading) — English only in 5.0. +- **Game metadata**: titles, descriptions, genres come from metadata providers. English is usually the source language; IGDB has some localisation but coverage is uneven. +- **Your own content**: collection names, notes, user-uploaded media. +- **Docs site** (what you're reading): English only in 5.0. ## Missing strings -If a translation is incomplete, you'll see the English fallback for untranslated strings. This is intentional — better than showing a broken UI in a half-translated locale. +If a translation is incomplete, you'll see the English fallback for untranslated strings. This is intentional: better than showing a broken UI in a half-translated locale. -Spotted something missing? Help translate it — see below. +Spotted something missing? Help translate it, see below. ## Contributing a translation @@ -55,17 +55,17 @@ Spotted something missing? Help translate it — see below. 1. Fork [rommapp/romm](https://github.com/rommapp/romm). 2. Under `frontend/src/locales//`, open the JSON / YAML files. -3. Add the missing keys — they're all present in `en_US` for reference. +3. Add the missing keys; they're all present in `en_US` for reference. 4. Open a PR. ### Add a new locale -1. Create `frontend/src/locales//` — copy the structure from `en_US`. +1. Create `frontend/src/locales//` by copying the structure from `en_US`. 2. Translate. -3. Register the locale in the `frontend/src/locales/index.ts` file (the filename may change — follow the pattern in the repo). +3. Register the locale in the `frontend/src/locales/index.ts` file (the filename may change; follow the pattern in the repo). 4. Open a PR. -Partial translations are welcome — we'd rather have a 70%-translated locale up for people to improve than hold out for 100%. +Partial translations are welcome; we'd rather have a 70%-translated locale up for people to improve than hold out for 100%. See [Translations (i18n)](../developers/i18n.md) for the full contributor walkthrough. @@ -81,14 +81,14 @@ No RTL locales in 5.0. Arabic and Hebrew translations may land in a future relea ### CJK typography -CJK locales (Japanese, Korean, Chinese simplified/traditional) use the browser's default system font stack. Some game titles with rare glyphs may render inconsistently across OSes — that's a browser font issue, not a RomM one. +CJK locales (Japanese, Korean, Chinese simplified/traditional) use the browser's default system font stack. Some game titles with rare glyphs may render inconsistently across OSes; that's a browser font issue, not a RomM one. ## Docs i18n -The docs site is currently English-only. Localising docs is a much bigger commitment than localising the app — if you'd like to help, open an issue on [rommapp/docs](https://github.com/rommapp/docs) and we can discuss scope. +The docs site is currently English-only. Localising docs is a much bigger commitment than localising the app; if you'd like to help, open an issue on [rommapp/docs](https://github.com/rommapp/docs) and we can discuss scope. ## Related -- [User Interface settings](account-and-profile.md) — full list of personal UI preferences. -- [Console Mode](console-mode.md) — same locale selection applies. -- [Developers → Translations (i18n)](../developers/i18n.md) — contributor guide. +- [User Interface settings](account-and-profile.md): full list of personal UI preferences. +- [Console Mode](console-mode.md): same locale selection applies. +- [Developers → Translations (i18n)](../developers/i18n.md): contributor guide. diff --git a/docs/using/library.md b/docs/using/library.md index c1909bf8..5ee5c2f2 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -1,6 +1,6 @@ --- title: Library -description: Browse your RomM library — dashboard, cards, filters, search, and the platform/game views. +description: Browse your RomM library: dashboard, cards, filters, search, and the platform/game views. --- @@ -13,28 +13,28 @@ The library is the heart of RomM. This page covers the day-to-day UI: the dashbo Home screen. Several **ribbons** of content: -- **Recently Added** — a carousel of the latest ROMs RomM has indexed. -- **Continue Playing** — games with an active play session. See [Play Sessions](../getting-started/concepts.md#play-session). -- **Platforms** — every platform that has at least one ROM. Cards link into the [platform view](#platform-view). -- **Collections** — your [collections](collections.md), [smart collections](smart-collections.md), and [virtual collections](virtual-collections.md). +- **Recently Added**: a carousel of the latest ROMs RomM has indexed. +- **Continue Playing**: games with an active play session. See [Play Sessions](../getting-started/concepts.md#play-session). +- **Platforms**: every platform that has at least one ROM. Cards link into the [platform view](#platform-view). +- **Collections**: your [collections](collections.md), [smart collections](smart-collections.md), and [virtual collections](virtual-collections.md). -Each ribbon can be hidden or shown from **Profile → User Interface** — handy if you prefer a tight dashboard. +Each ribbon can be hidden or shown from **Profile → User Interface**, handy if you prefer a tight dashboard. ## Menu bar Visible everywhere. Shortcuts to: -- **Search** — global search across every ROM, metadata field, and tag. -- **Platforms** — drawer listing every platform as a link. -- **Collections** — drawer listing every collection type. -- **Scan** — launches a scan. Permission-gated (Admin/Editor). -- **Console Mode** — jumps to the `/console` TV/gamepad UI. See [Console Mode](console-mode.md). -- **Upload** — permission-gated (Admin/Editor). See [Uploads](uploads.md). -- **Profile** — profile drawer + admin panel. +- **Search**: global search across every ROM, metadata field, and tag. +- **Platforms**: drawer listing every platform as a link. +- **Collections**: drawer listing every collection type. +- **Scan**: launches a scan. Permission-gated (Admin/Editor). +- **Console Mode**: jumps to the `/console` TV/gamepad UI. See [Console Mode](console-mode.md). +- **Upload**: permission-gated (Admin/Editor). See [Uploads](uploads.md). +- **Profile**: profile drawer + admin panel. ## Grid/list toggle -Every gallery (platform view, collection view, search results) has a grid-vs-list toggle in the top right. Grid is the default — card thumbnails at scale. List is denser — one row per ROM, sortable columns for title, release date, rating, playtime, region. +Every gallery (platform view, collection view, search results) has a grid-vs-list toggle in the top right. Grid is the default: card thumbnails at scale. List is denser: one row per ROM, sortable columns for title, release date, rating, playtime, region. ## Game cards @@ -42,46 +42,46 @@ Every gallery (platform view, collection view, search results) has a grid-vs-lis Hovering over a game card exposes: -- **Play** — launch in [EmulatorJS or Ruffle](in-browser-play.md), if the platform supports in-browser play. -- **Download** — single-file download. See [Downloads](downloads.md) for bulk, QR, and copy-link options. -- **Context menu (…)** — opens the card's action menu. +- **Play**: launch in [EmulatorJS or Ruffle](in-browser-play.md), if the platform supports in-browser play. +- **Download**: single-file download. See [Downloads](downloads.md) for bulk, QR, and copy-link options. +- **Context menu (…)**: opens the card's action menu. Card context menu: ![context menu](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/ContextMenu.png) -- **Match to metadata agent** — manually rematch against a provider (IGDB, ScreenScraper, etc.). -- **Edit** — change title, platform, or other metadata fields (permission-gated). -- **Refresh metadata** — re-query providers and overwrite. +- **Match to metadata agent**: manually rematch against a provider (IGDB, ScreenScraper, etc.). +- **Edit**: change title, platform, or other metadata fields (permission-gated). +- **Refresh metadata**: re-query providers and overwrite. - **Add/remove from Favourites**. -- **Add/remove from Collection** — surfaces your existing collections. +- **Add/remove from Collection**: surfaces your existing collections. Double-click (or tap) the card to open the [game view](#game-view). ## Filters -Every gallery has a Filters button in the top right. Filter combinations stack — RomM shows only games matching all active filters. +Every gallery has a Filters button in the top right. Filter combinations stack; RomM shows only games matching all active filters. ### Toggle filters -- **Show Unmatched** — games with no provider match. -- **Show Matched** — the inverse. -- **Show Favourites** — your personal favourites. -- **Show Duplicates** — games appearing more than once. -- **Show Playables** — only games with in-browser play support on this platform. -- **Show Missing** — DB entries whose files are gone. -- **Show Verified** — matched via Hasheous. -- **Show RetroAchievements** — games RetroAchievements has achievements for. See [RetroAchievements](retroachievements.md). +- **Show Unmatched**: games with no provider match. +- **Show Matched**: the inverse. +- **Show Favourites**: your personal favourites. +- **Show Duplicates**: games appearing more than once. +- **Show Playables**: only games with in-browser play support on this platform. +- **Show Missing**: DB entries whose files are gone. +- **Show Verified**: matched via Hasheous. +- **Show RetroAchievements**: games RetroAchievements has achievements for. See [RetroAchievements](retroachievements.md). ### Value filters Dropdowns that cross-reference metadata on the visible set: -- **Platform** — scope to one platform. -- **Genre / Franchise / Collections / Company / Age Rating / Region / Language** — metadata dimensions. -- **Status** — personal play status (Never Played, Backlogged, Playing, Complete, Hidden). +- **Platform**: scope to one platform. +- **Genre / Franchise / Collections / Company / Age Rating / Region / Language**: metadata dimensions. +- **Status**: personal play status (Never Played, Backlogged, Playing, Complete, Hidden). -Filters narrow down *what you're currently viewing*. Search first → filter second, and the filter dropdowns only show values present in the search results. +Filters narrow down *what you're currently viewing*. Search first, filter second, and the filter dropdowns only show values present in the search results. ## Search @@ -91,14 +91,14 @@ Click the search icon in the menu bar. Real-time search against: - Tags (regions, languages, revisions, arbitrary `[]`/`()` tags). - Metadata fields (genre, developer, publisher, summary). -Search is platform-aware — type `zelda` and you'll see every matched Zelda across every platform you have. +Search is platform-aware: type `zelda` and you'll see every matched Zelda across every platform you have. ![search bar](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/SearchBar.png) Two icons next to the search bar: -- **View filters** — same filter panel as above. -- **New collection from results** — saves the current search + filter state as a [standard collection](collections.md). +- **View filters**: same filter panel as above. +- **New collection from results**: saves the current search + filter state as a [standard collection](collections.md). ## Platform view @@ -108,40 +108,40 @@ Clicking a platform card takes you to the platform view: every game on that plat Two side buttons: -- **Platform drawer** — metadata for the platform itself: name, slug, category, generation, IGDB version, active providers, cover-art style setting. Admins see an **Upload** + **Scan** shortcut + a Danger Zone with "Delete Platform" (removes the DB entry; files on disk are not touched — a rescan re-creates the platform). -- **Firmware** — every firmware file registered against the platform, plus an upload button. See [Firmware Management](../administration/firmware-management.md). +- **Platform drawer**: metadata for the platform itself: name, slug, category, generation, IGDB version, active providers, cover-art style setting. Admins see an **Upload** + **Scan** shortcut + a Danger Zone with "Delete Platform" (removes the DB entry; files on disk are not touched; a rescan re-creates the platform). +- **Firmware**: every firmware file registered against the platform, plus an upload button. See [Firmware Management](../administration/firmware-management.md). ## Collection view Like the platform view, but scoped to one collection. Same filters and grid/list toggle. The side drawer shows collection metadata (name, owner, visibility, game count) and, for collections you own, Edit and Delete buttons. -Three collection types — see [Collections](collections.md), [Smart Collections](smart-collections.md), [Virtual Collections](virtual-collections.md). +Three collection types: see [Collections](collections.md), [Smart Collections](smart-collections.md), [Virtual Collections](virtual-collections.md). ## Game view Click (or tap) a game. The game view has two halves: -### Left — hero panel +### Left: hero panel - Cover art (or `vanilla-tilt` 3D hover if enabled in UI settings). - **Play** button (if playable). - **Download** button + menu (Copy Link, QR Code, Bulk Download). -- **Upload ROM** — replace / add a new version. -- Context menu — Match, Edit, Match rehash, Delete, etc. (permission-gated). +- **Upload ROM**: replace / add a new version. +- Context menu: Match, Edit, Match rehash, Delete, etc. (permission-gated). -### Right — tabs +### Right: tabs Which tabs appear depends on your metadata providers: -- **Details** — title, description, release date, genres, developer/publisher, regions, rating, matched providers. Filterable metadata surfaces here. -- **Game Data** — save files, save states, screenshots. Per-user. Upload, download, and delete. See [Saves & States](saves-and-states.md). -- **Personal** — your data on this game: status (Never Played / Backlogged / Playing / Complete / Hidden), rating, difficulty, percent complete, notes. Stored per-user. -- **Manual** — PDF viewer if you have a manual for this game. -- **Time to Beat** — [HowLongToBeat](../administration/metadata-providers.md#howlongtobeat) data if enabled. -- **Screenshots** — provider-fetched + user-uploaded screenshots. -- **Achievements** — [RetroAchievements](retroachievements.md) progression if you've linked your RA account. -- **Related Games** — similar titles from IGDB. -- **Additional Content** — DLCs, patches, hacks, mods, translations found inside the multi-file folder. +- **Details**: title, description, release date, genres, developer/publisher, regions, rating, matched providers. Filterable metadata surfaces here. +- **Game Data**: save files, save states, screenshots. Per-user. Upload, download, and delete. See [Saves & States](saves-and-states.md). +- **Personal**: your data on this game: status (Never Played / Backlogged / Playing / Complete / Hidden), rating, difficulty, percent complete, notes. Stored per-user. +- **Manual**: PDF viewer if you have a manual for this game. +- **Time to Beat**: [HowLongToBeat](../administration/metadata-providers.md#howlongtobeat) data if enabled. +- **Screenshots**: provider-fetched + user-uploaded screenshots. +- **Achievements**: [RetroAchievements](retroachievements.md) progression if you've linked your RA account. +- **Related Games**: similar titles from IGDB. +- **Additional Content**: DLCs, patches, hacks, mods, translations found inside the multi-file folder. ## Keyboard shortcuts diff --git a/docs/using/mobile-and-tv.md b/docs/using/mobile-and-tv.md index 6eece1d6..583ad620 100644 --- a/docs/using/mobile-and-tv.md +++ b/docs/using/mobile-and-tv.md @@ -1,34 +1,34 @@ --- title: Mobile & TV -description: Use RomM on phones, tablets, TVs, handhelds — recommended setups and companion apps. +description: Use RomM on phones, tablets, TVs, handhelds, with recommended setups and companion apps. --- # Mobile & TV -RomM is a web app. Anything with a modern browser can use it — phones, tablets, handhelds, TVs, set-top boxes. This page walks through the recommended setup for each form factor. +RomM is a web app. Anything with a modern browser can use it: phones, tablets, handhelds, TVs, set-top boxes. This page walks through the recommended setup for each form factor. ## Phones and tablets -### Option A — the PWA +### Option A: the PWA -Install RomM as a [Progressive Web App](pwa.md) — feels native, launches from the home screen, no app store. Recommended for most mobile users. +Install RomM as a [Progressive Web App](pwa.md): feels native, launches from the home screen, no app store. Recommended for most mobile users. - Good for: browsing, managing your library, downloading ROMs, in-browser play on devices with enough horsepower. - Limited by: no push notifications (yet), no OS-level file pickers for ROM uploads (use the web UI instead), Safari quirks on iOS. -### Option B — a native app +### Option B: a native app [Community-maintained mobile apps](../ecosystem/community-apps.md) exist: -- **Argosy Launcher** (Android) — official first-party. ROM syncing and launching. -- **romm-ios-app** / **romm-mobile** — community iOS and Android clients. +- **Argosy Launcher** (Android): official first-party. ROM syncing and launching. +- **romm-ios-app** / **romm-mobile**: community iOS and Android clients. Native apps give you proper file pickers, OS notifications, and nicer integration with device-level emulators (RetroArch mobile, Delta on iOS, etc.). ### Browser choice -- **Android** — Chrome, Firefox, or Samsung Internet. Chrome has best PWA support. -- **iOS** — Safari (the only way to install as PWA on iOS). +- **Android**: Chrome, Firefox, or Samsung Internet. Chrome has best PWA support. +- **iOS**: Safari (the only way to install as PWA on iOS). ### Touch vs mouse @@ -41,22 +41,22 @@ You've got a TV-attached Android box, a mini-PC, a Nvidia Shield, or a browser r ### The setup 1. **Install RomM as a PWA** on the device's browser. -2. **Set the default view to Console Mode** — Profile → User Interface → **Default view** → `/console`. -3. **Plug in a gamepad** — USB, Bluetooth, anything the browser sees. +2. **Set the default view to Console Mode**: Profile → User Interface → **Default view** → `/console`. +3. **Plug in a gamepad**: USB, Bluetooth, anything the browser sees. 4. **Launch the PWA.** You're in Console Mode, gamepad-ready. -Now it looks and feels like a console UI, running on a web page. Launching a game loads EmulatorJS full-screen — same gamepad passes through. +Now it looks and feels like a console UI, running on a web page. Launching a game loads EmulatorJS full-screen; same gamepad passes through. ### What about actual emulation? Most TVs and cheap Android boxes don't have the horsepower for heavy in-browser emulation (Saturn, Dreamcast, PSP). Two workarounds: -1. **Lighter cores** — EmulatorJS works fine on most platforms up to 16-bit and N64 on modest hardware. Stick to older systems and it's great. -2. **Native companion app** — install [RetroArch](https://retroarch.com/) or another emulator on the device, and sync ROMs/saves to it via a RomM companion app (Argosy, DeckRommSync, etc.). RomM's library management + your native emulator's playback. +1. **Lighter cores**: EmulatorJS works fine on most platforms up to 16-bit and N64 on modest hardware. Stick to older systems and it's great. +2. **Native companion app**: install [RetroArch](https://retroarch.com/) or another emulator on the device, and sync ROMs/saves to it via a RomM companion app (Argosy, DeckRommSync, etc.). RomM's library management + your native emulator's playback. ## Handhelds -Handhelds running custom firmware (muOS, Batocera, Knulli, ArkOS, JELOS, ROCKNIX) aren't traditional RomM clients — they don't run browsers well, and you probably don't want to play in one anyway. The integration pattern is: +Handhelds running custom firmware (muOS, Batocera, Knulli, ArkOS, JELOS, ROCKNIX) aren't traditional RomM clients. They don't run browsers well, and you probably don't want to play in one anyway. The integration pattern is: 1. **RomM hosts the library** on your home server. 2. **A companion app on the handheld** syncs ROMs, saves, and states to/from RomM. @@ -64,9 +64,9 @@ Handhelds running custom firmware (muOS, Batocera, Knulli, ArkOS, JELOS, ROCKNIX Recommended companions: -- **[Grout](../ecosystem/grout.md)** — first-party. muOS and NextUI handhelds. -- **[DeckRommSync](../ecosystem/community-apps.md)** — Steam Deck (SteamOS). -- **[SwitchRomM](../ecosystem/community-apps.md)** — Nintendo Switch (homebrew). +- **[Grout](../ecosystem/grout.md)**: first-party. muOS and NextUI handhelds. +- **[DeckRommSync](../ecosystem/community-apps.md)**: Steam Deck (SteamOS). +- **[SwitchRomM](../ecosystem/community-apps.md)**: Nintendo Switch (homebrew). All of these use [Client API Tokens](../ecosystem/client-api-tokens.md) for auth and the [Device Sync Protocol](../ecosystem/device-sync-protocol.md) for the actual data transfer. @@ -82,6 +82,7 @@ Any browser works. Use the PWA install flow (same as desktop) for a dedicated la Syncs ROMs and saves to the Deck's local library so RetroArch / EmuDeck picks them up natively. Set up once, then play without RomM in the loop. See [Community Apps](../ecosystem/community-apps.md). + Best combo: DeckRommSync for saves, plus the PWA for browsing / managing the library. ## Bandwidth considerations @@ -97,7 +98,7 @@ Some things are bandwidth-hungry, some aren't: | Device sync (saves) | Low | Saves are small; sync is fast. | | Device sync (ROMs) | High | Pushing full ROM sets to a handheld initially is a lot. | -On cellular? Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=false` (default — keep auth on) to avoid accidental discovery, and prefer native companion apps over in-browser play. +On cellular? Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=false` (default: keep auth on) to avoid accidental discovery, and prefer native companion apps over in-browser play. ## Self-hosting tips @@ -105,11 +106,11 @@ If you host RomM on a home server and want to reach it from cellular: - Put it behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). - Use a VPN (Tailscale, WireGuard) instead of exposing to the internet. Handhelds with Tailscale setups "just work". -- For public access without a VPN, put Cloudflare Access or similar zero-trust auth in front of RomM. Disable `ALLOW_PUBLIC_REGISTRATION` on RomM — the edge auth handles gatekeeping. +- For public access without a VPN, put Cloudflare Access or similar zero-trust auth in front of RomM. Disable `ALLOW_PUBLIC_REGISTRATION` on RomM; the edge auth handles gatekeeping. ## See also - [Install as PWA](pwa.md). - [Console Mode](console-mode.md). -- [Integrations & Ecosystem](../ecosystem/index.md) — every companion app RomM supports. -- [Community Apps](../ecosystem/community-apps.md) — the full list with platform / status flags. +- [Integrations & Ecosystem](../ecosystem/index.md): every companion app RomM supports. +- [Community Apps](../ecosystem/community-apps.md): the full list with platform / status flags. diff --git a/docs/using/netplay.md b/docs/using/netplay.md index f218e3b1..c343917e 100644 --- a/docs/using/netplay.md +++ b/docs/using/netplay.md @@ -1,11 +1,11 @@ --- title: Netplay -description: Play EmulatorJS games with friends in real time — hosting, joining, ICE servers, and NAT. +description: Play EmulatorJS games with friends in real time: hosting, joining, ICE servers, and NAT. --- # Netplay -**Netplay** lets you play [in-browser](in-browser-play.md) with other users in real time — co-op, turn-based, or party games, shared across the internet. EmulatorJS emulates "two players on one couch with two controllers", streaming video from the host to everyone else. +**Netplay** lets you play [in-browser](in-browser-play.md) with other users in real time: co-op, turn-based, or party games, shared across the internet. EmulatorJS emulates "two players on one couch with two controllers", streaming video from the host to everyone else. Best for 2-player co-op and turn-based games. Not ideal for twitchy fighting games (frame-level input isn't rollback-based). @@ -15,8 +15,8 @@ Best for 2-player co-op and turn-based games. Not ideal for twitchy fighting gam ## Prerequisites - EmulatorJS Netplay enabled in `config.yml` (operator-level). -- ICE servers configured (STUN + TURN) — without them, Netplay only works when all players are on the same LAN. -- All players need access to your RomM instance — Netplay doesn't proxy the ROM to people without RomM accounts. +- ICE servers configured (STUN + TURN); without them, Netplay only works when all players are on the same LAN. +- All players need access to your RomM instance. Netplay doesn't proxy the ROM to people without RomM accounts. See [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50) for the operator-side setup. @@ -28,7 +28,7 @@ See [Configuration File → `emulatorjs.netplay`](../reference/configuration-fil 4. **Create Room** → optional password. 5. Players who can reach your RomM see the room in the Netplay list. -You're Player 1. Up to three more players can join as Players 2–4 (core-dependent — some only support 2). +You're Player 1. Up to three more players can join as Players 2–4 (core-dependent; some only support 2). ## Joining a room @@ -41,14 +41,14 @@ Other players must be on **your RomM**. Netplay doesn't federate across instance ## Controls -Every player controls their own gamepad / keyboard locally — inputs are sent to the host, which runs the game and broadcasts video. Slight lag is expected over the internet (~50–150 ms typical); higher on transatlantic hops or with TURN in the path. +Every player controls their own gamepad / keyboard locally; inputs are sent to the host, which runs the game and broadcasts video. Slight lag is expected over the internet (~50–150 ms typical); higher on transatlantic hops or with TURN in the path. ## ICE servers (the NAT stuff) -WebRTC — the protocol Netplay uses — needs help to punch through consumer routers. That's what ICE (STUN + TURN) servers do: +WebRTC, the protocol Netplay uses, needs help to punch through consumer routers. That's what ICE (STUN + TURN) servers do: -- **STUN** — cheap; helps two peers find each other's public IPs. Works unless one of you is behind symmetric NAT. -- **TURN** — relays traffic when STUN can't. More resource-intensive; most free TURN services have quotas. +- **STUN**: cheap; helps two peers find each other's public IPs. Works unless one of you is behind symmetric NAT. +- **TURN**: relays traffic when STUN can't. More resource-intensive; most free TURN services have quotas. The operator wires ICE servers in `config.yml`: @@ -57,7 +57,7 @@ emulatorjs: netplay: enabled: true ice_servers: - # Google's free public STUN — no account needed + # Google's free public STUN, no account needed - urls: "stun:stun.l.google.com:19302" - urls: "stun:stun1.l.google.com:19302" # OpenRelay free TURN (rate-limited) @@ -73,9 +73,9 @@ For production Netplay, operators should get a dedicated TURN account (free tier ## Limitations -- **Not all cores support Netplay.** SNES9x, Mupen64Plus, Mednafen PSX, Genesis Plus GX — generally yes. Cores like PPSSPP or dosbox-pure — no. +- **Not all cores support Netplay.** SNES9x, Mupen64Plus, Mednafen PSX, Genesis Plus GX: generally yes. Cores like PPSSPP or dosbox-pure: no. - **Frame-perfect fighting isn't realistic.** Netplay is for casual co-op, not tournament-level fighting games. Use something like [FightCade](https://www.fightcade.com/) for that. -- **All players need RomM access.** There's no "join a friend's game without an account" — guests need at least a Viewer account on your instance. +- **All players need RomM access.** There's no "join a friend's game without an account"; guests need at least a Viewer account on your instance. - **RTC over TURN uses real bandwidth.** Hosting a 4-player N64 session over TURN can saturate a modest uplink. Prefer STUN where possible. ## Known caveat: nightly CDN @@ -90,13 +90,13 @@ Occasional hiccups (404s, untranslated UI strings) happen when the nightly is ou ## Security -Netplay room data is short-lived — no persistent record on RomM beyond the [Netplay Cleanup scheduled task](../administration/scheduled-tasks.md) sweep. Passwords protect room access but aren't stored long-term. +Netplay room data is short-lived, with no persistent record on RomM beyond the [Netplay Cleanup scheduled task](../administration/scheduled-tasks.md) sweep. Passwords protect room access but aren't stored long-term. ## Troubleshooting -- **"Failed to start game"** — Netplay server-side config is broken. Operator: check `docker logs romm | grep -i netplay`. Usually a misconfigured ICE server URL. -- **Other players can't see my room** — either they're not on your RomM (shouldn't happen if they have accounts) or the WebSocket connection is broken. See [WebSocket Troubleshooting](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). -- **Game plays fine locally but Netplay glitches** — network round-trip is too high. Test with Players on the same LAN first; add TURN and re-test from outside. -- **Audio desync** — known WebRTC issue; try a different browser (Chrome is most-tested), or restart the session. +- **"Failed to start game"**: Netplay server-side config is broken. Operator: check `docker logs romm | grep -i netplay`. Usually a misconfigured ICE server URL. +- **Other players can't see my room**: either they're not on your RomM (shouldn't happen if they have accounts) or the WebSocket connection is broken. See [WebSocket Troubleshooting](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). +- **Game plays fine locally but Netplay glitches**: network round-trip is too high. Test with Players on the same LAN first; add TURN and re-test from outside. +- **Audio desync**: known WebRTC issue; try a different browser (Chrome is most-tested), or restart the session. More in [Netplay Troubleshooting](../troubleshooting/netplay.md). diff --git a/docs/using/pwa.md b/docs/using/pwa.md index a71a646a..8a9bc09d 100644 --- a/docs/using/pwa.md +++ b/docs/using/pwa.md @@ -5,16 +5,16 @@ description: Add RomM to your phone or desktop home screen for an app-like exper # Install as PWA -RomM ships as a **Progressive Web App** — a proper manifest, service worker, and icons. You can install it to your phone or desktop and launch it like a native app. Same RomM, no browser chrome. +RomM ships as a **Progressive Web App**: a proper manifest, service worker, and icons. You can install it to your phone or desktop and launch it like a native app. Same RomM, no browser chrome. New in 5.0. ## Why install? -- **Looks and feels native** — icon on your home screen, fullscreen by default. -- **Faster startup** — the service worker caches the shell. -- **Mobile-friendly** — no browser address bar eating vertical space. -- **Offline-ish** — you can open the app without a network, though anything that actually fetches data still needs the server. +- **Looks and feels native**: icon on your home screen, fullscreen by default. +- **Faster startup**: the service worker caches the shell. +- **Mobile-friendly**: no browser address bar eating vertical space. +- **Offline-ish**: you can open the app without a network, though anything that actually fetches data still needs the server. ## Install on Android @@ -28,13 +28,13 @@ An icon appears on your home screen. Launching opens RomM full-screen. ### Samsung Internet / Other Android browsers -Similar — look for "Add to Home screen" in the share / menu. +Similar: look for "Add to Home screen" in the share / menu. ## Install on iOS iOS Safari is slightly different and has no true PWA parity (some APIs are missing), but the basic install flow works: -1. Open your RomM URL in **Safari** (not Chrome — on iOS, Chrome uses Safari's engine but doesn't expose the install flow). +1. Open your RomM URL in **Safari** (not Chrome; on iOS, Chrome uses Safari's engine but doesn't expose the install flow). 2. Tap the **Share** button (square with arrow). 3. **Add to Home Screen**. 4. Name it, tap **Add**. @@ -45,7 +45,7 @@ iOS limitations: - No push notifications. - Service worker caching is more aggressive / unpredictable than on Android. -- The browser UI is fully gone — only gesture-based navigation. +- The browser UI is fully gone; only gesture-based navigation. ## Install on desktop @@ -73,9 +73,9 @@ Same standalone window treatment as Chrome. ## Uninstalling -- **Android** — long-press the icon → **Uninstall** (or **Remove from Home screen**). -- **iOS** — long-press → **Remove App**. -- **Desktop** — inside the installed PWA window → three-dot menu → **Uninstall RomM**. +- **Android**: long-press the icon → **Uninstall** (or **Remove from Home screen**). +- **iOS**: long-press → **Remove App**. +- **Desktop**: inside the installed PWA window → three-dot menu → **Uninstall RomM**. Uninstalling just removes the shortcut and cached shell. Nothing server-side is touched. @@ -85,30 +85,30 @@ The service worker checks for new versions on launch. If RomM updates, the next If a stale shell is causing issues (e.g. seeing old UI after an upgrade), force-refresh: -- **Chrome/Edge mobile** — long-press the reload button → **Hard Reload**. -- **iOS Safari** — Settings → Safari → Clear History and Website Data → also removes the PWA cache. -- **Desktop PWAs** — the three-dot menu usually has a Reload option; hold Shift for a hard reload. +- **Chrome/Edge mobile**: long-press the reload button → **Hard Reload**. +- **iOS Safari**: Settings → Safari → Clear History and Website Data also removes the PWA cache. +- **Desktop PWAs**: the three-dot menu usually has a Reload option; hold Shift for a hard reload. ## Limitations -- **Requires HTTPS** — PWAs don't install from plain-HTTP hosts. Make sure your RomM is behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). -- **Icons** — RomM ships 192×192 and 512×512 manifest icons. Some devices pick a mid-size fallback that looks slightly blurry; known limitation, we'll expand the icon set over time. -- **No push notifications yet** — the PWA manifest doesn't register a notification handler in 5.0. Scan completion, task failures, etc. don't notify you. -- **Offline mode is partial** — opening the installed PWA offline shows the shell, but you can't actually browse the library or play anything without the server reachable. +- **Requires HTTPS**: PWAs don't install from plain-HTTP hosts. Make sure your RomM is behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). +- **Icons**: RomM ships 192×192 and 512×512 manifest icons. Some devices pick a mid-size fallback that looks slightly blurry; known limitation, we'll expand the icon set over time. +- **No push notifications yet**: the PWA manifest doesn't register a notification handler in 5.0. Scan completion, task failures, etc. don't notify you. +- **Offline mode is partial**: opening the installed PWA offline shows the shell, but you can't actually browse the library or play anything without the server reachable. ## Use with Console Mode PWA + [Console Mode](console-mode.md) is a powerful combo: -- Install the PWA on a TV-attached Android box → set default view to `/console`. +- Install the PWA on a TV-attached Android box, then set default view to `/console`. - Launching the app goes straight to a gamepad-friendly library. Feels like a console. ## Troubleshooting -- **No Install option in Chrome** — RomM isn't on HTTPS, or the manifest is missing/broken. Open devtools → Application → Manifest to diagnose. -- **Icon shows as a generic globe** — manifest icons aren't loading. Check the image URLs in devtools → Application → Manifest. -- **App won't open offline** — expected; only the shell caches, not data. Network access is required for everything useful. +- **No Install option in Chrome**: RomM isn't on HTTPS, or the manifest is missing/broken. Open devtools → Application → Manifest to diagnose. +- **Icon shows as a generic globe**: manifest icons aren't loading. Check the image URLs in devtools → Application → Manifest. +- **App won't open offline**: expected; only the shell caches, not data. Network access is required for everything useful. ## Want a native app instead? -Community-made native apps are listed on [Ecosystem → Community Apps](../ecosystem/community-apps.md) — including Argosy Launcher, RommBrowser, and more. PWA is the zero-install path; native apps give you platform integration (notifications, deeper OS hooks) where it matters. +Community-made native apps are listed on [Ecosystem → Community Apps](../ecosystem/community-apps.md), including Argosy Launcher, RommBrowser, and more. PWA is the zero-install path; native apps give you platform integration (notifications, deeper OS hooks) where it matters. diff --git a/docs/using/retroachievements.md b/docs/using/retroachievements.md index f25c9340..2f5988ca 100644 --- a/docs/using/retroachievements.md +++ b/docs/using/retroachievements.md @@ -5,13 +5,13 @@ description: Link your RetroAchievements account, surface progression, and brows # RetroAchievements -[RetroAchievements](https://retroachievements.org/) adds Xbox-style achievements to retro games. Thousands of classic titles have had achievement sets authored by the community — from NES through PSX and beyond. +[RetroAchievements](https://retroachievements.org/) adds Xbox-style achievements to retro games. Thousands of classic titles have had achievement sets authored by the community, from NES through PSX and beyond. When RomM is wired up to RetroAchievements, each user's progression (achievements unlocked, percentage complete, hardcore flag) is surfaced per-game in the UI. ## Prerequisites -1. **RomM operator** has to enable the provider — see [Metadata Providers → RetroAchievements](../administration/metadata-providers.md#retroachievements). Check by looking for the achievement tab on any known-supported game; if it's absent, the provider isn't active. +1. **RomM operator** has to enable the provider. See [Metadata Providers → RetroAchievements](../administration/metadata-providers.md#retroachievements). Check by looking for the achievement tab on any known-supported game; if it's absent, the provider isn't active. 2. **You** need a [RetroAchievements account](https://retroachievements.org/) and your personal **API key**. ## Linking your account @@ -21,9 +21,9 @@ When RomM is wired up to RetroAchievements, each user's progression (achievement 3. In RomM → **Profile** → **RetroAchievements** section → paste: - **Username**: your RA username (case-sensitive). - **API Key**: the key you just copied. -4. **Sync now** — RomM pulls your progression data and populates the Achievements tab on matched games. +4. **Sync now**: RomM pulls your progression data and populates the Achievements tab on matched games. -From this point on, RomM auto-syncs every user's progression via the nightly RetroAchievements Sync scheduled task — see [Scheduled Tasks](../administration/scheduled-tasks.md). No manual sync needed, but you can force one from the Profile page whenever. +From this point on, RomM auto-syncs every user's progression via the nightly RetroAchievements Sync scheduled task. See [Scheduled Tasks](../administration/scheduled-tasks.md). No manual sync needed, but you can force one from the Profile page whenever. ## Where it shows up @@ -43,53 +43,53 @@ A small badge appears on games where RA achievements exist, regardless of whethe ### Filters -- **Show RetroAchievements** — filter to games that have RA achievements. +- **Show RetroAchievements**: filter to games that have RA achievements. - You can combine with **Show Unmatched** or **Status: Playing** to find targets. ### Smart collections -[Smart collections](smart-collections.md) support a **Has Achievements** boolean field — build "Games with achievements I haven't started" with a rule combining this + your Personal status. +[Smart collections](smart-collections.md) support a **Has Achievements** boolean field. Build "Games with achievements I haven't started" with a rule combining this + your Personal status. ## Hardcore mode RetroAchievements distinguishes two play modes: -- **Softcore** — save states allowed. Achievements still count. -- **Hardcore** — no save states. More points per achievement; flagged separately. +- **Softcore**: save states allowed. Achievements still count. +- **Hardcore**: no save states. More points per achievement; flagged separately. -RomM doesn't enforce hardcore — you toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement, but marked as softcore. +RomM doesn't enforce hardcore; you toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement, but marked as softcore. If you care about hardcore, use the in-game save feature instead of save states. See [Saves & States → RetroAchievements and states](saves-and-states.md#retroachievements-and-states). ## Supported platforms -Most 8/16-bit consoles and some later platforms: NES, SNES, Genesis, Game Boy family, PC Engine, Master System, Atari 2600/7800, N64, PlayStation, Saturn, Dreamcast, and more. Some platforms are hash-dependent — your ROM has to match the RA canonical dump. +Most 8/16-bit consoles and some later platforms: NES, SNES, Genesis, Game Boy family, PC Engine, Master System, Atari 2600/7800, N64, PlayStation, Saturn, Dreamcast, and more. Some platforms are hash-dependent; your ROM has to match the RA canonical dump. Full up-to-date list + hash requirements: [RA consoles + games](https://retroachievements.org/systemList.php). ## Hash-based matching -Whether a ROM has RA support depends on the **hash** matching RA's database. Hash calculation runs as part of scans — if you've disabled hashing (`filesystem.skip_hash_calculation: true`), you lose RA matching. +Whether a ROM has RA support depends on the **hash** matching RA's database. Hash calculation runs as part of scans; if you've disabled hashing (`filesystem.skip_hash_calculation: true`), you lose RA matching. Some tips: - Prefer **No-Intro** / canonical dumps. Hacks, region-patched ROMs, or unusual dumps won't match. -- If a game you know has RA support shows no achievements, the hash is probably off. Try a different ROM source, or re-run a Hashes scan in [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). +- If a game you know has RA support shows no achievements, the hash is probably off. Try a different ROM source, or re-run a Hashes scan. See [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). ## Privacy - Your RA API key is stored server-side (per-user), encrypted at rest. -- RomM only calls RA using your key for *your* data — never shares across users. +- RomM only calls RA using your key for *your* data, never shares across users. - Admins can see which users have RA linked but not the API keys themselves. -To unlink: Profile → RetroAchievements → **Unlink** → confirm. Key is deleted; progression data stays cached until the next sync pass. +To unlink: Profile → RetroAchievements → **Unlink** → confirm. Key is deleted. Progression data stays cached until the next sync pass. ## Troubleshooting -- **"Invalid API key"** — regenerate on the RA settings page, paste fresh. -- **Games don't show the Achievements tab** — ROM hash doesn't match RA's canonical. Try a No-Intro dump. -- **Achievements show 0/N but you've unlocked some** — initial sync hasn't run yet. Click **Sync now** on your Profile page, or wait for the nightly. -- **Hardcore run didn't credit** — you probably loaded a save state mid-run. Start over and avoid states. +- **"Invalid API key"**: regenerate on the RA settings page, paste fresh. +- **Games don't show the Achievements tab**: ROM hash doesn't match RA's canonical. Try a No-Intro dump. +- **Achievements show 0/N but you've unlocked some**: initial sync hasn't run yet. Click **Sync now** on your Profile page, or wait for the nightly. +- **Hardcore run didn't credit**: you probably loaded a save state mid-run. Start over and avoid states. More in [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/using/rom-patcher.md b/docs/using/rom-patcher.md index d1355089..9d4f4f78 100644 --- a/docs/using/rom-patcher.md +++ b/docs/using/rom-patcher.md @@ -5,7 +5,7 @@ description: Apply IPS, UPS, BPS, PPF, and other patch formats to a ROM in the b # ROM Patcher -The **ROM Patcher** applies a patch file (a translation, hack, rebalance, no-intro fix) to a ROM **in your browser** — no CLI, no downloads, no uploading half-patched files by hand. +The **ROM Patcher** applies a patch file (a translation, hack, rebalance, no-intro fix) to a ROM **in your browser**: no CLI, no downloads, no uploading half-patched files by hand. New in 5.0. Powered by the `rom-patcher-js` library. @@ -23,7 +23,7 @@ New in 5.0. Powered by the `rom-patcher-js` library. | `.pmsr` | Paper Mario Star Rod. | | `.vcdiff` (`.xdelta`) | Generic binary diff. | -If your patch has an unusual extension, try renaming to one of the above — many are just different framings of the same underlying algorithm. +If your patch has an unusual extension, try renaming to one of the above; many are just different framings of the same underlying algorithm. ## Getting there @@ -31,13 +31,13 @@ Menu bar → **Patcher** icon (a wrench). Admins and Editors can also reach it f ## Patching workflow -1. **Upload the ROM** — drag and drop, or click to browse. The Patcher validates basic structure. -2. **Upload the patch** — same drag/drop zone below. -3. **Pick the target platform** — usually auto-detected from the ROM. +1. **Upload the ROM**: drag and drop, or click to browse. The Patcher validates basic structure. +2. **Upload the patch**: same drag/drop zone below. +3. **Pick the target platform**: usually auto-detected from the ROM. 4. **Choose output**: - - **Download patched ROM** — saves a file locally; RomM keeps neither the original nor the result. - - **Save to library** — adds the patched ROM as a new entry in your library, alongside the original. - - Optional **filename** — customise the output name. Defaults to ` [patched].`. + - **Download patched ROM**: saves a file locally; RomM keeps neither the original nor the result. + - **Save to library**: adds the patched ROM as a new entry in your library, alongside the original. + - Optional **filename**: customise the output name. Defaults to ` [patched].`. 5. **Apply**. Everything runs client-side in the browser. The ROM never gets uploaded to the RomM server during patching (if you picked Download); nothing leaves your machine. @@ -46,14 +46,14 @@ Everything runs client-side in the browser. The ROM never gets uploaded to the R When you pick **Save to library** instead of Download, RomM receives the final patched file and stores it as a new ROM. Goes in the same platform folder as the original, with the patched filename. A subsequent scan picks it up; a **Quick** scan is enough. -Metadata isn't inherited — the new ROM is **unmatched** until you run a scan. If the patch is listed on IGDB / ROMHacking.net as its own entry, matching may pick it up. Otherwise, match manually or tag with `(igdb-XXXX)` — see [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). +Metadata isn't inherited; the new ROM is **unmatched** until you run a scan. If the patch is listed on IGDB / ROMHacking.net as its own entry, matching may pick it up. Otherwise, match manually or tag with `(igdb-XXXX)`. See [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). ## Permissions | Action | Viewer | Editor | Admin | | --- | :---: | :---: | :---: | | Use Patcher → Download | ✓ | ✓ | ✓ | -| Use Patcher → Save to library | — | ✓ | ✓ | +| Use Patcher → Save to library | - | ✓ | ✓ | Saving to the library requires `roms.write` scope. @@ -61,11 +61,11 @@ Saving to the library requires `roms.write` scope. ### Translation patches -Many Japanese-only games have fan translations as IPS or BPS patches. Point the Patcher at the original ROM + translation, save to library, and you've got a new library entry alongside the original with the translated game — no clobbering. +Many Japanese-only games have fan translations as IPS or BPS patches. Point the Patcher at the original ROM + translation, save to library, and you've got a new library entry alongside the original with the translated game, no clobbering. ### ROM hacks and rebalances -Kaizo Mario-type hacks, FF6 balance patches, etc. — same flow. The patched ROM gets its own entry; your original stays intact. +Kaizo Mario-type hacks, FF6 balance patches, etc. Same flow: the patched ROM gets its own entry; your original stays intact. ### Regional fixes @@ -73,22 +73,22 @@ No-intro patches, region-free patches. Apply, download, and verify with the Hash ## Limits -- **Browser memory** — huge ROMs (PSX / Saturn / PSP ISOs, > 1 GB) can struggle in browsers with low memory limits. Safari is the most restrictive; Firefox / Chrome handle bigger files. -- **Multi-file ROMs** — the Patcher operates on a single file. For multi-disc games, patch each ISO separately. -- **Encrypted ROMs** — if the patch was authored against a decrypted ROM (e.g. DS `.nds` vs raw cartridge), your source has to match. -- **Save format changes** — patches that alter save-data layout will invalidate existing save files. Back them up before applying. +- **Browser memory**: huge ROMs (PSX / Saturn / PSP ISOs, > 1 GB) can struggle in browsers with low memory limits. Safari is the most restrictive; Firefox / Chrome handle bigger files. +- **Multi-file ROMs**: the Patcher operates on a single file. For multi-disc games, patch each ISO separately. +- **Encrypted ROMs**: if the patch was authored against a decrypted ROM (e.g. DS `.nds` vs raw cartridge), your source has to match. +- **Save format changes**: patches that alter save-data layout will invalidate existing save files. Back them up before applying. ## Verifying the result -The Patcher shows the pre-patch and post-patch hash (CRC32 + MD5) after apply — compare against whatever the patch author published. If hashes match: you've got the correct patched ROM. If not: wrong source ROM, wrong patch, or the patch archive was truncated. +The Patcher shows the pre-patch and post-patch hash (CRC32 + MD5) after apply; compare against whatever the patch author published. If hashes match: you've got the correct patched ROM. If not: wrong source ROM, wrong patch, or the patch archive was truncated. ## Troubleshooting -- **"Invalid patch format"** — wrong extension or corrupted file. Try re-downloading the patch. -- **"Checksum mismatch"** — the patch was built against a different ROM revision than yours. Common with no-intro vs TOSEC dumps. Look up the patch author's expected source. -- **Browser hangs on huge ROMs** — close other tabs, try Firefox, or patch outside the browser with `Floating IPS` or `BPS` CLI tools then upload the result. -- **"Save to library" produces a broken ROM** — usually the patcher succeeded but the file was corrupted in transit. Retry; if it persists, patch externally and upload. +- **"Invalid patch format"**: wrong extension or corrupted file. Try re-downloading the patch. +- **"Checksum mismatch"**: the patch was built against a different ROM revision than yours. Common with no-intro vs TOSEC dumps. Look up the patch author's expected source. +- **Browser hangs on huge ROMs**: close other tabs, try Firefox, or patch outside the browser with `Floating IPS` or `BPS` CLI tools then upload the result. +- **"Save to library" produces a broken ROM**: usually the patcher succeeded but the file was corrupted in transit. Retry; if it persists, patch externally and upload. ## API -ROM patching is a client-side feature — no dedicated backend endpoints. When you "save to library", the patched file is uploaded via the standard chunked-upload flow (`POST /api/roms/upload/start` → `PUT /api/roms/upload/{id}` → `POST /api/roms/upload/{id}/complete`). See [Uploads → API](uploads.md#api). +ROM patching is a client-side feature with no dedicated backend endpoints. When you "save to library", the patched file is uploaded via the standard chunked-upload flow (`POST /api/roms/upload/start` → `PUT /api/roms/upload/{id}` → `POST /api/roms/upload/{id}/complete`). See [Uploads → API](uploads.md#api). diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md index 143096e5..96762e01 100644 --- a/docs/using/saves-and-states.md +++ b/docs/using/saves-and-states.md @@ -7,8 +7,8 @@ description: Manage save files and save-states across in-browser play and synced Two different things, often confused: -- **Save files** — the in-game save (`.srm`, `.sav`, `.save`, etc.). What the game writes when you use the in-game save feature. Works across emulator cores that share the format. -- **Save states** — a full memory snapshot of the emulator at a moment in time. Emulator-specific; a SNES9x state won't load in bsnes. +- **Save files**: the in-game save (`.srm`, `.sav`, `.save`, etc.). What the game writes when you use the in-game save feature. Works across emulator cores that share the format. +- **Save states**: a full memory snapshot of the emulator at a moment in time. Emulator-specific; a SNES9x state won't load in bsnes. Both are **per-user per-ROM** and stored under `/romm/assets///`. They follow you across browsers and devices. @@ -16,8 +16,8 @@ Both are **per-user per-ROM** and stored under `/romm/assets///`. The Game detail page → **Game Data** tab. Separate sub-tabs for: -- **Saves** — all save files for this ROM, with date and emulator. -- **States** — all states, with thumbnail (if one was captured). +- **Saves**: all save files for this ROM, with date and emulator. +- **States**: all states, with thumbnail (if one was captured). ## Uploading a save @@ -32,23 +32,23 @@ The save is available to [in-browser play](in-browser-play.md) on the next launc ## Uploading a state -Same flow under **Upload State**. Optional screenshot attachment — RomM autogenerates one when you create a state from in-browser play; only matters when uploading from outside. +Same flow under **Upload State**. Optional screenshot attachment; RomM autogenerates one when you create a state from in-browser play. Only matters when uploading from outside. ## Creating during play In-game Menu in EmulatorJS: -- **Save** — writes in-game save data back to RomM. Same as the native console's save-to-cartridge flow. -- **Save State** / **Load State** — creates a full memory snapshot, or restores from one. RomM uploads the snapshot automatically. +- **Save**: writes in-game save data back to RomM. Same as the native console's save-to-cartridge flow. +- **Save State** / **Load State**: creates a full memory snapshot, or restores from one. RomM uploads the snapshot automatically. -There's no "forgot to upload" step — everything persists as soon as you do it in-emulator. +There's no "forgot to upload" step; everything persists as soon as you do it in-emulator. ## Selecting on launch If a ROM has multiple saves or states, the pre-launch picker appears before the emulator loads: -- **Save file** dropdown — which save to load on boot. -- **State** dropdown — optional; loads immediately after boot. +- **Save file** dropdown: which save to load on boot. +- **State** dropdown: optional; loads immediately after boot. [Console Mode](console-mode.md) surfaces the same picker on a larger gamepad-friendly dialog. @@ -70,11 +70,11 @@ For bulk cleanup (e.g. "delete every state for games I've beaten"), use the mult Saves and states can sync to/from registered devices (Grout on muOS, DeckRommSync on a Deck, etc.). Covered in depth in the ecosystem section: -- [Device Sync Protocol](../ecosystem/device-sync-protocol.md) — wire-level reference. -- [SSH Sync](../administration/ssh-sync.md) — operator-side config. -- [Argosy Launcher](../ecosystem/argosy.md) / [Grout](../ecosystem/grout.md) — per-app setup. +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level reference. +- [SSH Sync](../administration/ssh-sync.md): operator-side config. +- [Argosy Launcher](../ecosystem/argosy.md) / [Grout](../ecosystem/grout.md): per-app setup. -From the end-user side: once your device is paired and sync is running, saves made on the device appear in RomM within a couple of sync cycles (default: 15 minutes). Conflicts — same ROM saved on two devices between syncs — surface as two separate save entries; pick which to keep. +From the end-user side: once your device is paired and sync is running, saves made on the device appear in RomM within a couple of sync cycles (default: 15 minutes). Conflicts (same ROM saved on two devices between syncs) surface as two separate save entries; pick which to keep. ## Format / core compatibility @@ -88,7 +88,7 @@ Save files are usually format-interchangeable across cores for the same platform | SNES | `.srm` | Yes, across SNES9x / bsnes | | Genesis / Mega Drive | `.srm` | Yes | | Game Boy / GBC / GBA | `.sav` | Yes, across Gambatte / mGBA | -| N64 | `.srm`, `.eep`, `.fla` | Yes, but per-type — the right file must be uploaded | +| N64 | `.srm`, `.eep`, `.fla` | Yes, but per-type: the right file must be uploaded | | PSX | `.srm` (memory card) | Yes, across Mednafen / PCSX cores | | Saturn / Dreamcast | Varies | Check core docs | @@ -110,10 +110,10 @@ When you create a state via in-game Menu → Save State, EmulatorJS grabs a fram ## Troubleshooting -- **Save uploaded but the game doesn't see it** — wrong format for the core. Check the compatibility table above; re-upload or switch cores. -- **State loads a corrupted frame** — state was saved by a different build of the core. If RomM updated the emulator bundle, old states may not load cleanly. Re-create or start a fresh save. -- **Save disappears after play** — the emulator didn't flush on quit. Use in-game **Save and Quit** instead of just closing the browser. -- **Can't upload — "file too large"** — reverse proxy limit; raise `client_max_body_size` / `proxy-body-size`. See [Reverse Proxy](../install/reverse-proxy.md). +- **Save uploaded but the game doesn't see it**: wrong format for the core. Check the compatibility table above; re-upload or switch cores. +- **State loads a corrupted frame**: state was saved by a different build of the core. If RomM updated the emulator bundle, old states may not load cleanly. Re-create or start a fresh save. +- **Save disappears after play**: the emulator didn't flush on quit. Use in-game **Save and Quit** instead of just closing the browser. +- **Can't upload, "file too large"**: reverse proxy limit; raise `client_max_body_size` / `proxy-body-size`. See [Reverse Proxy](../install/reverse-proxy.md). More in [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/using/smart-collections.md b/docs/using/smart-collections.md index 4d81a340..71df4ddd 100644 --- a/docs/using/smart-collections.md +++ b/docs/using/smart-collections.md @@ -1,11 +1,11 @@ --- title: Smart Collections -description: Rule-based collections that auto-populate from your library — new in 5.0. +description: Rule-based collections that auto-populate from your library, new in 5.0. --- # Smart Collections -A **smart collection** is a collection defined by **rules**, not by hand-picking. You describe what's in it ("all SNES games rated ≥4 stars", "everything in the Zelda franchise", "games I've beaten"), and RomM keeps the list in sync automatically — as you add ROMs, update ratings, mark games complete, the collection updates. +A **smart collection** is a collection defined by **rules**, not by hand-picking. You describe what's in it ("all SNES games rated ≥4 stars", "everything in the Zelda franchise", "games I've beaten"), and RomM keeps the list in sync automatically: as you add ROMs, update ratings, mark games complete, the collection updates. New in 5.0. For hand-curated collections, see [Collections](collections.md). For auto-generated-by-RomM groupings, see [Virtual Collections](virtual-collections.md). @@ -13,10 +13,10 @@ New in 5.0. For hand-curated collections, see [Collections](collections.md). For 1. **Collections** drawer → **+ New Smart Collection**. 2. Name it. -3. Build the rule set — add one or more **conditions**. +3. Build the rule set by adding one or more **conditions**. 4. Save. -The collection is live immediately — populated with every ROM currently matching the rules, and updated on every relevant change afterward. +The collection is live immediately: populated with every ROM currently matching the rules, and updated on every relevant change afterward. ## Rule structure @@ -24,40 +24,40 @@ A smart collection is one or more **conditions** joined by **all** (AND) or **an Each condition has three parts: -1. **Field** — what you're matching on. -2. **Operator** — comparison (equals, contains, greater-than, etc.). -3. **Value** — the thing you're comparing against. +1. **Field**: what you're matching on. +2. **Operator**: comparison (equals, contains, greater-than, etc.). +3. **Value**: the thing you're comparing against. ## Supported fields ### Metadata -- **Platform** — platform slug. -- **Title** — game title (case-insensitive substring match with `contains`). -- **Genre** — IGDB genre tag. -- **Franchise** — game franchise (Mario, Final Fantasy, etc.). -- **Developer** — company that made the game. -- **Publisher** — company that released it. -- **Release Year** — year. -- **Age Rating** — ESRB / PEGI rating. -- **Region** — game region (USA, Japan, Europe, World, etc.). -- **Language** — game language. -- **Rating** — IGDB / ScreenScraper critic score. +- **Platform**: platform slug. +- **Title**: game title (case-insensitive substring match with `contains`). +- **Genre**: IGDB genre tag. +- **Franchise**: game franchise (Mario, Final Fantasy, etc.). +- **Developer**: company that made the game. +- **Publisher**: company that released it. +- **Release Year**: year. +- **Age Rating**: ESRB / PEGI rating. +- **Region**: game region (USA, Japan, Europe, World, etc.). +- **Language**: game language. +- **Rating**: IGDB / ScreenScraper critic score. ### Personal data -- **Personal Rating** — your per-game rating. -- **Status** — Never Played / Backlogged / Playing / Complete / Hidden. -- **Playtime** — accumulated play time (minutes). -- **Favourites** — whether you've favourited it. -- **Has Achievements** — whether the game has [RetroAchievements](retroachievements.md). +- **Personal Rating**: your per-game rating. +- **Status**: Never Played / Backlogged / Playing / Complete / Hidden. +- **Playtime**: accumulated play time (minutes). +- **Favourites**: whether you've favourited it. +- **Has Achievements**: whether the game has [RetroAchievements](retroachievements.md). ### Flags -- **Matched** — has a provider ID. -- **Playable in browser** — supports EmulatorJS / Ruffle. -- **Has Firmware** — required firmware exists in the library. -- **Duplicate** — the same game appears twice. +- **Matched**: has a provider ID. +- **Playable in browser**: supports EmulatorJS / Ruffle. +- **Has Firmware**: required firmware exists in the library. +- **Duplicate**: the same game appears twice. ## Operators @@ -108,14 +108,14 @@ any of: - Title contains "zelda" ``` -(Combine `all of` + `any of` by nesting — the rule editor supports groups.) +(Combine `all of` + `any of` by nesting; the rule editor supports groups.) ## Public / private Same visibility model as standard collections: -- **Private** — only you see it (your personal data fields matter). -- **Public** — everyone on the instance sees it. *Your* personal-data rules still apply — if your smart collection is "games I haven't finished", every user sees *your* unfinished games. +- **Private**: only you see it (your personal data fields matter). +- **Public**: everyone on the instance sees it. *Your* personal-data rules still apply; if your smart collection is "games I haven't finished", every user sees *your* unfinished games. For shared rule sets across users, use metadata-only fields and keep the collection public. Rules that reference Personal data (status, rating, playtime, favourites) only make sense as private collections. @@ -123,23 +123,23 @@ For shared rule sets across users, use metadata-only fields and keep the collect Smart collections refresh: -- **Live** — when you add, remove, rate, or edit a ROM, RomM re-evaluates rules instantly. -- **On scan** — after every scan, rules are re-evaluated against the new state. -- **No manual refresh needed** — but admins can trigger a full re-evaluation via **Administration → Tasks → Refresh Smart Collections** if something looks stale. +- **Live**: when you add, remove, rate, or edit a ROM, RomM re-evaluates rules instantly. +- **On scan**: after every scan, rules are re-evaluated against the new state. +- **No manual refresh needed**, but admins can trigger a full re-evaluation via **Administration → Tasks → Refresh Smart Collections** if something looks stale. ## Editing a rule -Open the smart collection → drawer → **Edit**. The rule builder reopens with current rules pre-loaded. Save → collection updates immediately. +Open the smart collection → drawer → **Edit**. The rule builder reopens with current rules pre-loaded. Save, and the collection updates immediately. ## Deleting -Same as standard collections — removes the definition. ROMs stay in the library. +Same as standard collections: removes the definition. ROMs stay in the library. ## Limitations -- **No nested smart collections** — a smart collection can't reference another collection as a source. Compose rules directly. -- **Performance** — very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible; mentioned here for completeness. -- **Timezone** — "Release Year" uses UTC, not the user's timezone. Edge-case edge-of-year games might fall on the "wrong" side. +- **No nested smart collections**: a smart collection can't reference another collection as a source. Compose rules directly. +- **Performance**: very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible; mentioned here for completeness. +- **Timezone**: "Release Year" uses UTC, not the user's timezone. Edge-case edge-of-year games might fall on the "wrong" side. ## API @@ -151,4 +151,4 @@ PUT /api/collections/smart/{id} # update DELETE /api/collections/smart/{id} # delete ``` -Rule schema is part of the POST body — see the [API Reference](../developers/api-reference.md) for the JSON structure. Requires `collections.read` / `collections.write`. +Rule schema is part of the POST body; see the [API Reference](../developers/api-reference.md) for the JSON structure. Requires `collections.read` / `collections.write`. diff --git a/docs/using/uploads.md b/docs/using/uploads.md index 0c03b5fe..f567530c 100644 --- a/docs/using/uploads.md +++ b/docs/using/uploads.md @@ -33,7 +33,7 @@ Multiple files upload in parallel. Progress bars show per-file status; failures Files over 64 MB are uploaded in chunks. RomM: 1. Opens an upload session (`POST /api/roms/upload/start`). -2. Streams chunks (`PUT /api/roms/upload/{id}`) — 64 MB each. +2. Streams chunks (`PUT /api/roms/upload/{id}`), 64 MB each. 3. Finalises the upload (`POST /api/roms/upload/{id}/complete`), which assembles the file and indexes it. A browser refresh mid-upload cancels the session and cleans up partial data. @@ -43,15 +43,15 @@ A browser refresh mid-upload cancels the session and cleans up partial data. To upload a multi-file game (multi-disc, game + DLC): 1. Zip the whole game folder on your end. -2. Upload the zip — RomM detects the structure and unpacks to a folder under the platform directory. +2. Upload the zip: RomM detects the structure and unpacks to a folder under the platform directory. 3. The resulting folder follows the [multi-file game convention](../getting-started/folder-structure.md#multi-file-games). ### After upload Uploaded ROMs are immediately visible in the platform view, but aren't **matched** to metadata until the next scan. Two options: -- **Run a Quick scan** — picks up new files only. -- **Match manually** — game detail → Match → search for the title. +- **Run a Quick scan**: picks up new files only. +- **Match manually**: game detail → Match → search for the title. ## Save uploads @@ -62,7 +62,7 @@ For your own games. Useful for importing existing save files from another emulat 3. Optionally attach a screenshot (a thumbnail for the save). 4. Save. -The file's stored under `/romm/assets///saves/` and is available to your [in-browser play](in-browser-play.md) sessions. RomM doesn't rewrite the file contents — it's stored as-is. +The file's stored under `/romm/assets///saves/` and is available to your [in-browser play](in-browser-play.md) sessions. RomM doesn't rewrite the file contents; it's stored as-is. See [Saves & States](saves-and-states.md) for more. @@ -70,8 +70,8 @@ See [Saves & States](saves-and-states.md) for more. Emulator save-states. Same flow as saves but under **States**. -- State formats are emulator-specific — a SNES9x state won't load in bsnes. -- Screenshots can be attached — RomM generates one automatically from the emulator if you create a state from [in-browser play](in-browser-play.md). +- State formats are emulator-specific; a SNES9x state won't load in bsnes. +- Screenshots can be attached; RomM generates one automatically from the emulator if you create a state from [in-browser play](in-browser-play.md). ## Screenshot uploads @@ -79,11 +79,11 @@ Personal screenshot uploads attach to a ROM (not to a save/state). Great for bui Game detail → **Screenshots** tab → **Upload**. -Supported formats: PNG, JPEG, WebP. RomM converts on ingest to WebP via the Image Conversion task — see [Scheduled Tasks](../administration/scheduled-tasks.md). +Supported formats: PNG, JPEG, WebP. RomM converts on ingest to WebP via the Image Conversion task. See [Scheduled Tasks](../administration/scheduled-tasks.md). ## Firmware uploads -Admin / Editor only. See [Firmware Management](../administration/firmware-management.md) for the full flow — two paths (drop in folder + scan, or UI upload), per-platform organisation, file naming requirements. +Admin / Editor only. See [Firmware Management](../administration/firmware-management.md) for the full flow: two paths (drop in folder + scan, or UI upload), per-platform organisation, file naming requirements. ## Manual uploads @@ -93,25 +93,25 @@ Supported: PDF. Renders in the browser via the Manual tab. ## Cover art uploads -Admin / Editor. Game detail → Context menu (…) → **Edit** → **Upload cover** → PNG / JPG / WebP. Overrides provider-fetched cover. To revert: Edit → **Reset cover** → the next metadata refresh repopulates from providers. +Admin / Editor. Game detail → Context menu (…) → **Edit** → **Upload cover** → PNG / JPG / WebP. Overrides provider-fetched cover. To revert: Edit → **Reset cover**; the next metadata refresh repopulates from providers. ## Permissions summary | Action | Viewer | Editor | Admin | | --- | :---: | :---: | :---: | | Upload save/state/screenshot for own account | ✓ | ✓ | ✓ | -| Upload ROMs | — | ✓ | ✓ | -| Upload firmware | — | ✓ | ✓ | -| Upload manual / cover art | — | ✓ | ✓ | +| Upload ROMs | - | ✓ | ✓ | +| Upload firmware | - | ✓ | ✓ | +| Upload manual / cover art | - | ✓ | ✓ | Full scope matrix in [Users & Roles](../administration/users-and-roles.md#scope-matrix). ## Troubleshooting -- **`413 Request Entity Too Large`** — your reverse proxy or ingress is capping body size. See [Reverse Proxy](../install/reverse-proxy.md) for the `client_max_body_size 0` / `proxy-body-size: "0"` fix. -- **Upload progresses then fails at 99%** — the finalise step timed out. Usually reverse-proxy read timeout is too tight; raise it. -- **"File is not a valid ROM for this platform"** — RomM's extension check rejected the file. Either it's the wrong platform, or the extension is obscure — see [Folder Structure → Naming](../getting-started/folder-structure.md#naming-convention). -- **Save upload silently doesn't appear in-emulator** — you uploaded to the wrong emulator core. Check [Saves & States → Emulator compatibility](saves-and-states.md) for the format → core matrix. +- **`413 Request Entity Too Large`**: your reverse proxy or ingress is capping body size. See [Reverse Proxy](../install/reverse-proxy.md) for the `client_max_body_size 0` / `proxy-body-size: "0"` fix. +- **Upload progresses then fails at 99%**: the finalise step timed out. Usually reverse-proxy read timeout is too tight; raise it. +- **"File is not a valid ROM for this platform"**: RomM's extension check rejected the file. Either it's the wrong platform, or the extension is obscure. See [Folder Structure → Naming](../getting-started/folder-structure.md#naming-convention). +- **Save upload silently doesn't appear in-emulator**: you uploaded to the wrong emulator core. Check [Saves & States → Emulator compatibility](saves-and-states.md) for the format → core matrix. ## API diff --git a/docs/using/virtual-collections.md b/docs/using/virtual-collections.md index 6c9c26c7..44994240 100644 --- a/docs/using/virtual-collections.md +++ b/docs/using/virtual-collections.md @@ -1,17 +1,17 @@ --- title: Virtual Collections -description: Auto-generated groupings by genre, developer, year, tag — no rules to write. +description: Auto-generated groupings by genre, developer, year, tag, with no rules to write. --- # Virtual Collections -**Virtual collections** are auto-generated by RomM. You don't create or edit them — RomM looks at your library and groups ROMs by common metadata dimensions. +**Virtual collections** are auto-generated by RomM. You don't create or edit them; RomM looks at your library and groups ROMs by common metadata dimensions. New in 5.0. Compare with: -- [Collections](collections.md) — you pick each ROM by hand. -- [Smart Collections](smart-collections.md) — you write rules, RomM populates. -- **Virtual Collections** — RomM picks both the groupings *and* the contents. +- [Collections](collections.md): you pick each ROM by hand. +- [Smart Collections](smart-collections.md): you write rules, RomM populates. +- **Virtual Collections**: RomM picks both the groupings *and* the contents. ## What gets grouped @@ -27,9 +27,9 @@ Virtual collections are generated for several dimensions: | **Decade** | "1980s", "1990s", "2000s" | | **Age Rating** | "ESRB: E", "PEGI: 7+" | | **Region** | "USA", "Japan", "Europe" | -| **Tags** | Any `[]`/`()` tag from filenames — see [Folder Structure → Naming convention](../getting-started/folder-structure.md#naming-convention) | +| **Tags** | Any `[]`/`()` tag from filenames, see [Folder Structure → Naming convention](../getting-started/folder-structure.md#naming-convention) | -Collections with too few ROMs are suppressed — you won't see a "Developer: Some Indie Studio" with one entry. +Collections with too few ROMs are suppressed; you won't see a "Developer: Some Indie Studio" with one entry. ## Turning them on and off @@ -45,17 +45,17 @@ Virtual collections show up alongside regular and smart collections in: - The **Collections** drawer. - The dashboard's **Collections** ribbon (if enabled). -- The filter panel under "Collection" — so you can filter any gallery by virtual collection membership. +- The filter panel under "Collection", so you can filter any gallery by virtual collection membership. -Open one like any collection — grid/list view, filters, play, download, etc. +Open one like any collection: grid/list view, filters, play, download, etc. ## What you *can't* do Virtual collections are read-only. You can't: - Add a ROM by hand (use a [standard collection](collections.md)). -- Remove a ROM (its metadata determines membership — fix the metadata if it's wrong). -- Make one "public" vs "private" — they're per-user, always visible to you. +- Remove a ROM (its metadata determines membership; fix the metadata if it's wrong). +- Make one "public" vs "private" (they're per-user, always visible to you). - Rename them (they take their name from the metadata dimension). - Set a custom cover image. @@ -70,14 +70,14 @@ Virtual collections recompute on every scan and on every metadata edit. No manua Almost always metadata. A game missing from "Virtual Collection: RPG" probably doesn't have the `RPG` genre set on its IGDB entry. Options: 1. **Manually edit the genre** on the game (permission-gated). -2. **Run an Unmatched scan** — sometimes IGDB has updated. -3. **Leave it** — the rest of the genre's collection still works. +2. **Run an Unmatched scan**: sometimes IGDB has updated. +3. **Leave it**: the rest of the genre's collection still works. Genre coverage varies by metadata provider: -- **IGDB** — rich genre data. -- **ScreenScraper** — ok genre data; some provider-specific labels. -- **Hasheous / PlayMatch** (hash-only) — no genre data; they proxy IGDB. +- **IGDB**: rich genre data. +- **ScreenScraper**: ok genre data; some provider-specific labels. +- **Hasheous / PlayMatch** (hash-only): no genre data; they proxy IGDB. If your library is heavily ScreenScraper-matched and you want rich genres, add IGDB and run an **Unmatched** or **Update Metadata** scan. See [Metadata Providers](../administration/metadata-providers.md). @@ -91,6 +91,6 @@ GET /api/collections/virtual?type=genre # filter by dimension GET /api/collections/virtual/{id} # get one ``` -There's no POST / PUT / DELETE — RomM owns the lifecycle. +There's no POST / PUT / DELETE; RomM owns the lifecycle. Requires `collections.read` scope. See the [API Reference](../developers/api-reference.md). From 80ca7f51e8ea764ce469697a9b2b609157812c00 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 18:18:12 -0400 Subject: [PATCH 029/121] reduce semicolons in prose: split or reword Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/about/credits.md | 4 +-- docs/about/faqs.md | 10 +++---- docs/about/license.md | 8 +++--- docs/administration/administration-page.md | 14 +++++----- docs/administration/authentication.md | 10 +++---- docs/administration/firmware-management.md | 12 ++++---- .../invitations-and-registration.md | 12 ++++---- docs/administration/metadata-providers.md | 18 ++++++------ docs/administration/observability.md | 4 +-- docs/administration/oidc/authelia.md | 2 +- docs/administration/oidc/authentik.md | 6 ++-- docs/administration/oidc/index.md | 10 +++---- docs/administration/oidc/keycloak.md | 6 ++-- docs/administration/oidc/pocketid.md | 6 ++-- docs/administration/oidc/zitadel.md | 8 +++--- docs/administration/scanning-and-watcher.md | 14 +++++----- docs/administration/scheduled-tasks.md | 14 +++++----- docs/administration/server-stats.md | 14 +++++----- docs/administration/ssh-sync.md | 4 +-- docs/administration/users-and-roles.md | 14 +++++----- docs/developers/api-authentication.md | 12 ++++---- docs/developers/api-reference.md | 6 ++-- docs/developers/architecture.md | 8 +++--- docs/developers/contributing.md | 10 +++---- docs/developers/development-setup.md | 10 +++---- docs/developers/i18n.md | 20 ++++++------- docs/developers/index.md | 2 +- docs/developers/openapi.md | 6 ++-- docs/developers/releasing.md | 12 ++++---- docs/developers/websockets.md | 12 ++++---- docs/ecosystem/argosy.md | 4 +-- docs/ecosystem/client-api-tokens.md | 14 +++++----- docs/ecosystem/community-apps.md | 6 ++-- docs/ecosystem/device-sync-protocol.md | 12 ++++---- docs/ecosystem/fpkgi.md | 8 +++--- docs/ecosystem/grout.md | 12 ++++---- docs/ecosystem/igir.md | 2 +- docs/ecosystem/index.md | 4 +-- docs/ecosystem/kekatsu.md | 6 ++-- docs/ecosystem/muos-app.md | 12 ++++---- docs/ecosystem/pkgj.md | 8 +++--- docs/ecosystem/tinfoil.md | 10 +++---- docs/ecosystem/webrcade.md | 6 ++-- docs/getting-started/concepts.md | 20 ++++++------- docs/getting-started/first-scan.md | 8 +++--- docs/getting-started/folder-structure.md | 4 +-- docs/getting-started/quick-start.md | 6 ++-- docs/getting-started/what-is-new-in-5.md | 4 +-- docs/index.md | 4 +-- docs/install/backup-and-restore.md | 10 +++---- docs/install/databases.md | 12 ++++---- docs/install/docker-compose.md | 18 ++++++------ docs/install/image-variants.md | 8 +++--- docs/install/index.md | 2 +- docs/install/kubernetes.md | 10 +++---- docs/install/redis-or-valkey.md | 10 +++---- docs/install/reverse-proxy.md | 6 ++-- docs/install/synology.md | 8 +++--- docs/install/truenas.md | 10 +++---- docs/install/unraid.md | 2 +- docs/platforms/custom-platforms.md | 8 +++--- docs/platforms/emulatorjs-config.md | 10 +++---- docs/platforms/firmware-by-platform.md | 8 +++--- docs/platforms/ms-dos.md | 8 +++--- docs/platforms/ruffle-config.md | 10 +++---- docs/platforms/supported-platforms.md | 2 +- docs/reference/configuration-file.md | 10 +++---- docs/reference/exports.md | 2 +- docs/reference/glossary.md | 6 ++-- docs/reference/ports-and-endpoints.md | 12 ++++---- docs/reference/scheduled-tasks.md | 2 +- docs/releases/changelog.md | 4 +-- docs/releases/index.md | 10 +++---- docs/releases/upgrading-to-3.0.md | 14 +++++----- docs/releases/upgrading-to-5.0.md | 28 +++++++++---------- docs/resources/snippets/scheduled-tasks.md | 2 +- docs/scripts/gen_platforms.py | 2 +- docs/scripts/gen_scheduled_tasks.py | 4 +-- docs/troubleshooting/authentication.md | 16 +++++------ docs/troubleshooting/in-browser-play.md | 8 +++--- docs/troubleshooting/index.md | 6 ++-- docs/troubleshooting/kubernetes.md | 4 +-- docs/troubleshooting/miscellaneous.md | 6 ++-- docs/troubleshooting/netplay.md | 16 +++++------ docs/troubleshooting/scanning.md | 8 +++--- docs/troubleshooting/sync.md | 16 +++++------ docs/troubleshooting/synology.md | 6 ++-- docs/using/account-and-profile.md | 20 ++++++------- docs/using/collections.md | 8 +++--- docs/using/console-mode.md | 10 +++---- docs/using/downloads.md | 8 +++--- docs/using/in-browser-play.md | 12 ++++---- docs/using/languages.md | 16 +++++------ docs/using/library.md | 4 +-- docs/using/mobile-and-tv.md | 10 +++---- docs/using/netplay.md | 16 +++++------ docs/using/pwa.md | 14 +++++----- docs/using/retroachievements.md | 10 +++---- docs/using/rom-patcher.md | 18 ++++++------ docs/using/saves-and-states.md | 14 +++++----- docs/using/smart-collections.md | 8 +++--- docs/using/uploads.md | 16 +++++------ docs/using/virtual-collections.md | 14 +++++----- 103 files changed, 480 insertions(+), 480 deletions(-) diff --git a/docs/about/credits.md b/docs/about/credits.md index 76ef3b59..7d644bec 100644 --- a/docs/about/credits.md +++ b/docs/about/credits.md @@ -9,7 +9,7 @@ RomM exists because a lot of people contributed code, designs, translations, ide ## Core maintainers -The small team that reviews PRs, cuts releases, and keeps the project moving. Check [rommapp/romm](https://github.com/rommapp/romm/graphs/contributors) for the current active list; credits here would drift. +The small team that reviews PRs, cuts releases, and keeps the project moving. Check [rommapp/romm](https://github.com/rommapp/romm/graphs/contributors) for the current active list. Credits here would drift. ## Contributors @@ -94,7 +94,7 @@ Donors via [Open Collective](https://opencollective.com/romm) make continued dev ## Missing? -Open a PR against this page. Credit is cheap; we'd rather err on the side of naming everyone than leaving someone out. +Open a PR against this page. Credit is cheap, and we'd rather err on the side of naming everyone than leaving someone out. ## See also diff --git a/docs/about/faqs.md b/docs/about/faqs.md index 463278de..09d1c39c 100644 --- a/docs/about/faqs.md +++ b/docs/about/faqs.md @@ -13,7 +13,7 @@ See [Introduction](../index.md) for the full pitch. ## Is it free? -Yes. [AGPL-3.0](license.md). Core always will be free; other repos in the umbrella use permissive licenses. No tracking, no upsells. +Yes. [AGPL-3.0](license.md). Core always will be free, and other repos in the umbrella use permissive licenses. No tracking, no upsells. ## How does it compare to [X other manager]? @@ -21,7 +21,7 @@ Not a direct comparison page. Short version: RomM emphasises self-hosted + multi ## Do I need metadata API keys? -Not strictly. RomM runs without any; games just won't match to a metadata source, so no covers, descriptions, or ratings. +Not strictly. RomM runs without any, but games just won't match to a metadata source, so no covers, descriptions, or ratings. Recommended: IGDB + ScreenScraper. See [Metadata Providers](../administration/metadata-providers.md) for the full list. @@ -41,9 +41,9 @@ See [Install & Deploy](../install/index.md). Not supported: -- Bare metal without containers (not documented; may work). -- TrueNAS CORE (FreeBSD; no Docker). -- Windows without WSL (Docker Desktop works; bare Windows doesn't). +- Bare metal without containers (not documented, but may work). +- TrueNAS CORE (FreeBSD, no Docker). +- Windows without WSL (Docker Desktop works, but bare Windows doesn't). ## How much RAM / CPU does it need? diff --git a/docs/about/license.md b/docs/about/license.md index 38927e58..db25655d 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -35,13 +35,13 @@ The RomM umbrella hosts several projects under different licenses: | [rommapp/muos-app](https://github.com/rommapp/muos-app) | check repo | | [rommapp/docs](https://github.com/rommapp/docs) (what you're reading) | CC0 | -Companion repos use more permissive licenses (GPL-3.0 or MIT) because they're smaller, more-replaceable, and don't host the library; the AGPL network-service clause doesn't offer the same protection benefits there. +Companion repos use more permissive licenses (GPL-3.0 or MIT) because they're smaller, more-replaceable, and don't host the library. The AGPL network-service clause doesn't offer the same protection benefits there. -Docs (this site) are CC0: do whatever you want with the content; attribution appreciated but not required. +Docs (this site) are CC0: do whatever you want with the content. Attribution appreciated but not required. ## Third-party components -RomM ships several third-party components with their own licenses: [EmulatorJS](https://emulatorjs.org/), [Ruffle](https://ruffle.rs/), Vue, FastAPI, and a long list of smaller dependencies. Their licenses apply to their respective code; none of them override AGPL-3.0 on the RomM code itself. +RomM ships several third-party components with their own licenses: [EmulatorJS](https://emulatorjs.org/), [Ruffle](https://ruffle.rs/), Vue, FastAPI, and a long list of smaller dependencies. Their licenses apply to their respective code, and none of them override AGPL-3.0 on the RomM code itself. Full list via `uv tree` in the backend and `npm ls` in the frontend. Redistribution respects each upstream's terms. @@ -53,7 +53,7 @@ Yes. AGPL doesn't restrict private or commercial use. ### Can I fork RomM and relicense my fork? -No. AGPL is a strong copyleft; forks remain AGPL. You can add your own changes under AGPL, but can't relicense the original code. +No. AGPL is a strong copyleft, so forks remain AGPL. You can add your own changes under AGPL, but can't relicense the original code. ### Can I charge money for RomM-as-a-service? diff --git a/docs/administration/administration-page.md b/docs/administration/administration-page.md index 5284f452..c982cfd7 100644 --- a/docs/administration/administration-page.md +++ b/docs/administration/administration-page.md @@ -5,9 +5,9 @@ description: A tour of the in-app Administration UI, where every operator contro # Administration Page -Click your **profile avatar** (top right, any page) to open the settings drawer. The links you see depend on your role. Admins see everything; Editors and Viewers see a subset. +Click your **profile avatar** (top right, any page) to open the settings drawer. The links you see depend on your role. Admins see everything, and Editors and Viewers see a subset. -This page is a map of what's behind each link. The deep mechanics of each feature live on their own pages; this is where to click. +This page is a map of what's behind each link. The deep mechanics of each feature live on their own pages. This is where to click. ## The drawer @@ -16,7 +16,7 @@ This page is a map of what's behind each link. The deep mechanics of each featur | **Profile** | Everyone | Change own username, email, password, avatar. Link a RetroAchievements account and sync achievements. | | **User Interface** | Everyone | Locale, theme (dark/light/auto), game card layout, home dashboard ribbons, collection display settings. | | **Library Management** | Editors + Admins | Platform bindings & version mappings, missing-ROMs tool, library folder settings. | -| **Metadata Sources** | Admins | Credentials for the 13 metadata providers; scan priority. | +| **Metadata Sources** | Admins | Credentials for the 13 metadata providers, scan priority. | | **Administration** | Admins | Users, Client API Tokens, Tasks. The main admin hub. | | **Client API Tokens** | Everyone (own tokens) | Each user's personal API tokens. Admins see a separate "all tokens" view under Administration. | | **Server Stats** | Admins | Numbers: platforms, games, saves, states, screenshots, disk usage. | @@ -27,8 +27,8 @@ This page is a map of what's behind each link. The deep mechanics of each featur The thing every user touches. - **Username / email / password**: self-serve changes. Password changes require the current password. -- **Avatar**: upload a small image; displayed next to your name everywhere. -- **RetroAchievements**: set your RA username to link accounts; "Sync now" pulls fresh progression data. +- **Avatar**: upload a small image, displayed next to your name everywhere. +- **RetroAchievements**: set your RA username to link accounts. "Sync now" pulls fresh progression data. See [Users & Roles](users-and-roles.md) for what role-specific self-serve is allowed. @@ -36,7 +36,7 @@ See [Users & Roles](users-and-roles.md) for what role-specific self-serve is all Per-user UI preferences. Stored in the user's row + localStorage, not in `config.yml`. -- **Language**: 19 locales supported; see [Languages](../using/languages.md) for the list. +- **Language**: 19 locales supported. See [Languages](../using/languages.md) for the list. - **Theme**: Dark, Light, or Auto (follows OS preference). Palette overrides via `extra_css` are operator-level. - **Game card layout**: cover style (2D, 3D boxart, poster), info-density, the `vanilla-tilt` 3D hover effect on/off. - **Home dashboard ribbons**: show/hide "Recently Added", "Continue Playing", "Collections", etc. @@ -70,7 +70,7 @@ The main admin hub. Three sub-panels: ### Client API Tokens -- Table of every token on the server (admin view; users see only their own via their Profile). +- Table of every token on the server (admin view, users see only their own via their Profile). - Filter by user. Revoke any token. - See [Authentication → Client API Tokens](authentication.md#client-api-tokens) for the create-your-own flow. diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index 8291c084..d715b383 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -21,7 +21,7 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: | Variable | Default | What it controls | | --- | --- | --- | -| `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually**; it invalidates every active session and every outstanding invite link. | +| `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually**, because it invalidates every active session and every outstanding invite link. | | `ROMM_AUTH_SECRET_KEY_FILE` | _unset_ | Alternative: read the secret from a file. Useful with Docker secrets. | | `SESSION_MAX_AGE_SECONDS` | 86400 (24 h) | How long a session cookie lives before the user has to sign in again. | | `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | 900 (15 min) | Short-lived OAuth2 access token TTL. | @@ -30,7 +30,7 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: ## Local (username + password) -The default. Accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md). Passwords are bcrypt-hashed; RomM does not log or store plaintext passwords at any point. +The default. Accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md). Passwords are bcrypt-hashed, and RomM does not log or store plaintext passwords at any point. **Disable local password login entirely** (force OIDC-only): @@ -48,11 +48,11 @@ Until email-based self-serve reset lands, admins set passwords manually: **Administration → Users → Edit → New password → Save.** -The next login on that account will use the new password; existing sessions for that user remain valid until they expire; revoke them explicitly by deleting all of the user's Client API Tokens if that's a concern. +The next login on that account will use the new password. Existing sessions for that user remain valid until they expire. Revoke them explicitly by deleting all of the user's Client API Tokens if that's a concern. ### Self-serve password reset -Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA; the UI path exists and will light up once email config is exposed.) +Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA, but the UI path exists and will light up once email config is exposed.) ## OIDC @@ -98,7 +98,7 @@ Appropriate for: - Public-facing "display" instances (e.g. a wall-mounted browse-only catalogue). - `demo.romm.app`. -Authenticated users (when you do sign in) still see their full role; kiosk only affects anonymous traffic. +Authenticated users (when you do sign in) still see their full role. Kiosk only affects anonymous traffic. ## Download-endpoint auth bypass diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index 118207b8..39b77eff 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -20,7 +20,7 @@ Legality varies by jurisdiction. RomM does not ship firmware and the project can The usual workflow if you have firmware on disk already. -1. Put the file in the right `bios/` folder (Structure A or B; see [Folder Structure](../getting-started/folder-structure.md)). +1. Put the file in the right `bios/` folder (Structure A or B, see [Folder Structure](../getting-started/folder-structure.md)). 2. Run a scan. Firmware is picked up alongside ROMs. 3. It shows up in **Administration → Library Management → Firmware**. @@ -33,11 +33,11 @@ When you don't have shell access or you're uploading from a different machine. 3. Drag and drop the file, or click to browse. 4. RomM puts it in the correct `bios/{platform}/` directory on disk. -Editors and Admins can upload firmware; Viewers can't (`firmware.write` scope required, see [Users & Roles](users-and-roles.md)). +Editors and Admins can upload firmware, but Viewers can't (`firmware.write` scope required, see [Users & Roles](users-and-roles.md)). ## Platform-specific firmware -Every emulator has its own requirements: which files it needs, specific hashes, naming conventions. The [Supported Platforms](../platforms/supported-platforms.md) table flags which platforms need firmware for EmulatorJS playback; a dedicated [Firmware by Platform](../platforms/firmware-by-platform.md) page lists what's needed for the popular ones. +Every emulator has its own requirements: which files it needs, specific hashes, naming conventions. The [Supported Platforms](../platforms/supported-platforms.md) table flags which platforms need firmware for EmulatorJS playback, and a dedicated [Firmware by Platform](../platforms/firmware-by-platform.md) page lists what's needed for the popular ones. Common examples: @@ -78,15 +78,15 @@ Requires `firmware.read` / `firmware.write` scopes. See the [API Reference](../d When a user launches a platform that requires firmware in EmulatorJS: 1. The player checks RomM for a matching firmware file. -2. If present, it's served directly; no user action required. +2. If present, it's served directly. No user action required. 3. If missing, the player surfaces an error ("firmware required") and the admin needs to upload one. Configure which emulator settings to expose to users via `emulatorjs.settings` in [`config.yml`](../reference/configuration-file.md). Details on the player side in [In-Browser Play](../using/in-browser-play.md). ## Integration with companion apps -Handheld syncers (Grout, Argosy, DeckRommSync) can pull firmware alongside ROMs. They use the same Client API Token auth as for ROMs; scope the token to include `firmware.read`. See [Client API Tokens](../ecosystem/client-api-tokens.md). +Handheld syncers (Grout, Argosy, DeckRommSync) can pull firmware alongside ROMs. They use the same Client API Token auth as for ROMs, so scope the token to include `firmware.read`. See [Client API Tokens](../ecosystem/client-api-tokens.md). ## Backups -Firmware is user-owned data: back it up. `/romm/library/bios/` is part of your library mount; if you're backing up the library volume, firmware is already covered. See [Backup & Restore](../install/backup-and-restore.md). +Firmware is user-owned data: back it up. `/romm/library/bios/` is part of your library mount, so if you're backing up the library volume, firmware is already covered. See [Backup & Restore](../install/backup-and-restore.md). diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md index 5d8c834c..0c88cc99 100644 --- a/docs/administration/invitations-and-registration.md +++ b/docs/administration/invitations-and-registration.md @@ -14,7 +14,7 @@ Three ways a new account ends up on a RomM instance: ## First-user setup -When a fresh RomM container starts against an empty database, hitting any page redirects to the **Setup Wizard**. The wizard collects a username, email, and password; the resulting account is **always an Admin**, regardless of any env var. +When a fresh RomM container starts against an empty database, hitting any page redirects to the **Setup Wizard**. The wizard collects a username, email, and password. The resulting account is **always an Admin**, regardless of any env var. To skip the wizard (e.g. when provisioning via automation and you'll create users through the API), set: @@ -23,14 +23,14 @@ environment: - DISABLE_SETUP_WIZARD=true ``` -You'll then need to create the first admin via the API or by injecting a row at deploy time; the UI won't offer a setup flow. +You'll then need to create the first admin via the API or by injecting a row at deploy time, because the UI won't offer a setup flow. ## Invite links The recommended way to add users, because it avoids you ever touching their password. 1. **Administration → Users → Invite.** Pick a role (Viewer, Editor, Admin). -2. RomM generates a single-use URL; copy it and send it to the invitee. +2. RomM generates a single-use URL. Copy it and send it to the invitee. 3. When they open it, they pick their own username and password. RomM creates the account with the role you chose and logs them straight in. Invite tokens are **single-use** and **time-limited**. Defaults: @@ -39,10 +39,10 @@ Invite tokens are **single-use** and **time-limited**. Defaults: | --- | --- | --- | | Expiry | 30 days | `INVITE_TOKEN_DAYS` | -Expired links return a clear error on the `/register` page; generate a new one from the Users panel. +Expired links return a clear error on the `/register` page. Generate a new one from the Users panel. !!! tip "Invitations over HTTPS" - Invite URLs include a signed token; they're not useful to anyone without RomM's `ROMM_AUTH_SECRET_KEY`. Still, send them over a trusted channel; once someone has a valid invite URL, they can claim the account. + Invite URLs include a signed token, so they're not useful to anyone without RomM's `ROMM_AUTH_SECRET_KEY`. Still, send them over a trusted channel, because once someone has a valid invite URL, they can claim the account. ## Public self-registration @@ -73,7 +73,7 @@ Anyone who signs up this way is a Viewer. Promote them manually from **Administr | Public registration (`ALLOW_PUBLIC_REGISTRATION=true`) | Viewer | | OIDC first login | Default Viewer, or mapped from claims via `OIDC_CLAIM_ROLES` | -Changing a user's role afterwards is a normal admin action; see [Users & Roles](users-and-roles.md). +Changing a user's role afterwards is a normal admin action. See [Users & Roles](users-and-roles.md). ## Password reset diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md index f6f2ae00..35efac43 100644 --- a/docs/administration/metadata-providers.md +++ b/docs/administration/metadata-providers.md @@ -7,7 +7,7 @@ description: Configure the thirteen metadata sources RomM supports: IGDB, Screen RomM pulls game metadata (titles, descriptions, cover art, screenshots, manuals, achievement data, completion times) from up to **thirteen** providers. You don't need all of them. This page covers the recommended combinations and per-provider setup. -Configure providers either via env vars (below) or interactively in **Administration → Metadata Sources** in the UI. Scan priority (which provider wins when two disagree) is set in [`config.yml`](../reference/configuration-file.md); see `scan.priority.metadata` and `scan.priority.artwork`. +Configure providers either via env vars (below) or interactively in **Administration → Metadata Sources** in the UI. Scan priority (which provider wins when two disagree) is set in [`config.yml`](../reference/configuration-file.md). See `scan.priority.metadata` and `scan.priority.artwork`. ## Popular combos @@ -51,7 +51,7 @@ Configure providers either via env vars (below) or interactively in **Administra Access requires a Twitch account and a phone number for 2FA. Up-to-date instructions live in the [IGDB API docs](https://api-docs.igdb.com/#account-creation). When registering your application in the Twitch Developer Portal: -- **Name**: something unique; picking an existing name fails silently. Use `romm-`. +- **Name**: something unique, because picking an existing name fails silently. Use `romm-`. - **OAuth Redirect URLs**: `localhost` - **Category**: Application Integration - **Client Type**: Confidential @@ -77,7 +77,7 @@ Metadata, cover art, and screenshots. [Create an account](https://www.mobygames. ### SteamGridDB -[SteamGridDB](https://www.steamgriddb.com/) serves custom cover art for games and collections. It's not used by the scanner directly; it surfaces in the **Search Cover** button when you manually edit a game's artwork. +[SteamGridDB](https://www.steamgriddb.com/) serves custom cover art for games and collections. It's not used by the scanner directly. It surfaces in the **Search Cover** button when you manually edit a game's artwork. Log in with a [Steam account](https://store.steampowered.com/join), go to your [API tab](https://www.steamgriddb.com/profile/preferences/api), and set `STEAMGRIDDB_API_KEY`. @@ -85,9 +85,9 @@ Log in with a [Steam account](https://store.steampowered.com/join), go to your [ [RetroAchievements](https://retroachievements.org/) provides achievement data and hash matching. Generate a web API key from your RA [settings page](https://retroachievements.org/settings) and set `RETROACHIEVEMENTS_API_KEY`. Run an **Unmatched** scan on the platforms you want matched. -Each RomM user also links their own RA username in their profile to sync personal progression; a new **Achievements** tab appears on the **Personal** data panel once linked. +Each RomM user also links their own RA username in their profile to sync personal progression. A new **Achievements** tab appears on the **Personal** data panel once linked. -The RA database is cached locally; refresh frequency is controlled by `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` (default: 30). +The RA database is cached locally. Refresh frequency is controlled by `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` (default: 30). ??? info "Screenshots" ![RA API key](../resources/metadata_providers/1-ra.png) @@ -112,7 +112,7 @@ environment: - SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON=0 5 * * * # default: 5am daily ``` -Run at least one LaunchBox update (manually from the Scan page, or wait for the cron) before using it as a scan source; RomM won't match against an empty local DB. +Run at least one LaunchBox update (manually from the Scan page, or wait for the cron) before using it as a scan source, because RomM won't match against an empty local DB. ### TheGamesDB @@ -159,11 +159,11 @@ Two edits in the ES-DE settings file so ES-DE writes its metadata and media into ``` -`MediaDirectory` puts artwork next to ROMs; `LegacyGamelistFileLocation` writes `gamelist.xml` next to ROMs instead of in the ES-DE config folder. If you already have scraped assets, move the contents of `~/ES-DE/downloaded_media/` and `~/ES-DE/gamelists/` into the ROM folders. +`MediaDirectory` puts artwork next to ROMs, and `LegacyGamelistFileLocation` writes `gamelist.xml` next to ROMs instead of in the ES-DE config folder. If you already have scraped assets, move the contents of `~/ES-DE/downloaded_media/` and `~/ES-DE/gamelists/` into the ROM folders. ### Libretro -Libretro's retro core metadata is used internally for platform mapping and fallback artwork: no env flag, no credentials. Nothing to configure; RomM uses it automatically when it knows the libretro core for a platform. +Libretro's retro core metadata is used internally for platform mapping and fallback artwork: no env flag, no credentials. Nothing to configure. RomM uses it automatically when it knows the libretro core for a platform. ## Metadata tags in filenames @@ -178,7 +178,7 @@ RomM honours inline tags in ROM filenames to force a match against a specific pr | `(launchbox-xxxx)` | [LaunchBox](https://gamesdb.launchbox-app.com/) | | `(hltb-xxxx)` | [HowLongToBeat](https://howlongtobeat.com/) | -RomM will **not** rename your files to add these; they're opt-in, and renaming would conflict with other tooling that walks the filesystem. +RomM will **not** rename your files to add these. They're opt-in, and renaming would conflict with other tooling that walks the filesystem. ## Priority and conflict resolution diff --git a/docs/administration/observability.md b/docs/administration/observability.md index a9af817f..e9f79de5 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -23,7 +23,7 @@ environment: - NO_COLOR=1 # 1 to disable colour entirely ``` -`INFO` is the default and sane for production. Drop to `DEBUG` only while debugging a specific issue; RomM is chatty on DEBUG. +`INFO` is the default and sane for production. Drop to `DEBUG` only while debugging a specific issue, because RomM is chatty on DEBUG. ### Reading logs @@ -142,7 +142,7 @@ Returns an array of every scheduled / manual / watcher task with current status - **Don't parse unstructured log lines** for metrics. Use OTEL or `/api/tasks/status`. - **Don't log at DEBUG in production.** The volume is real and scans will drown in it. -- **Don't scrape HTML pages for health checks.** `/api/heartbeat` is the contract; HTML changes between versions, the API endpoint is stable. +- **Don't scrape HTML pages for health checks.** `/api/heartbeat` is the contract. HTML changes between versions, the API endpoint is stable. ## Minimum recommended stack diff --git a/docs/administration/oidc/authelia.md b/docs/administration/oidc/authelia.md index c1f26069..77c73fac 100644 --- a/docs/administration/oidc/authelia.md +++ b/docs/administration/oidc/authelia.md @@ -7,7 +7,7 @@ description: Wire RomM's SSO to Authelia: claims policy, client registration, Ro [Authelia](https://www.authelia.com/) is a lightweight open-source authentication and authorisation server with two-factor auth and SSO. Good fit for homelabs that already proxy through a reverse proxy. -Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. ## 1. Prerequisites diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md index 27752d4f..b9376f96 100644 --- a/docs/administration/oidc/authentik.md +++ b/docs/administration/oidc/authentik.md @@ -7,7 +7,7 @@ description: Wire RomM's SSO to Authentik: property mapping for email_verified, [Authentik](https://goauthentik.io/) is a full-featured open-source IdP with MFA, flows, and a sizeable audit/admin surface. Good fit for users who want more than Authelia offers. -Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -55,7 +55,7 @@ Configure: - **Authorization flow**: implicit consent - **Redirect URIs**: `https://romm.example.com/api/oauth/openid` -Copy the generated **Client ID** and **Client Secret**; you'll use them as `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` on the RomM side. +Copy the generated **Client ID** and **Client Secret**. You'll use them as `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` on the RomM side. ![Provider settings](../../resources/authentik/4-provider-secrets.png) @@ -100,7 +100,7 @@ In RomM → **Profile** → set your email to exactly the same address Authentik ## 7. Test -Restart RomM and open `/login`. Click the **Login with OIDC** button; you're redirected to Authentik, authenticate, and come back signed into RomM. +Restart RomM and open `/login`. Click the **Login with OIDC** button. You're redirected to Authentik, authenticate, and come back signed into RomM. ![Login with OIDC](../../resources/authentik/8-romm-login.png) diff --git a/docs/administration/oidc/index.md b/docs/administration/oidc/index.md index 63b90418..0994a372 100644 --- a/docs/administration/oidc/index.md +++ b/docs/administration/oidc/index.md @@ -21,11 +21,11 @@ OpenID Connect (OIDC) lets users sign in to RomM through an external identity pr ## Provider guides -Pick your provider and follow the step-by-step. They all end with the same set of env vars on the RomM side; the guides just differ on how to register RomM as an application and where to find the client ID/secret. +Pick your provider and follow the step-by-step. They all end with the same set of env vars on the RomM side. The guides just differ on how to register RomM as an application and where to find the client ID/secret. - [Authelia](authelia.md): lightweight self-hosted IdP, great for homelabs. - [Authentik](authentik.md): full-featured open-source IdP with MFA and fancy flows. -- [Keycloak](keycloak.md): the heavyweight standard; feature-complete. +- [Keycloak](keycloak.md): the heavyweight standard, feature-complete. - [PocketID](pocketid.md): passkey-only, minimalist. - [Zitadel](zitadel.md): enterprise-grade open source with SAML + OIDC. @@ -60,13 +60,13 @@ environment: - OIDC_ROLE_ADMIN=romm-admin,platform-admins ``` -On every login, RomM reads the claim named by `OIDC_CLAIM_ROLES` (often `groups`, sometimes `realm_access.roles` on Keycloak; check your provider's token output). Whichever role has a matching value wins; if nothing matches, the user stays/becomes a Viewer. +On every login, RomM reads the claim named by `OIDC_CLAIM_ROLES` (often `groups`, sometimes `realm_access.roles` on Keycloak, check your provider's token output). Whichever role has a matching value wins. If nothing matches, the user stays/becomes a Viewer. Roles are re-evaluated on **every login**, so demoting someone on the IdP side takes effect the next time they sign in. ## Autologin -Bypass the RomM login page entirely; redirects straight to the IdP: +Bypass the RomM login page entirely. Redirects straight to the IdP: ```yaml environment: @@ -88,7 +88,7 @@ environment: - OIDC_END_SESSION_ENDPOINT=https://auth.example.com/application/o/end-session/ ``` -The endpoint URL is provider-specific; the per-provider guides list it. +The endpoint URL is provider-specific. The per-provider guides list it. ## Username source diff --git a/docs/administration/oidc/keycloak.md b/docs/administration/oidc/keycloak.md index cd1fa8aa..6689a36b 100644 --- a/docs/administration/oidc/keycloak.md +++ b/docs/administration/oidc/keycloak.md @@ -5,9 +5,9 @@ description: Wire RomM's SSO to Keycloak: realm, client, RomM env vars, optional # OIDC with Keycloak -[Keycloak](https://www.keycloak.org/) is the heavyweight open-source IAM standard. Feature-complete, well-documented, widely deployed; the default choice when your SSO needs are serious. +[Keycloak](https://www.keycloak.org/) is the heavyweight open-source IAM standard. Feature-complete, well-documented, widely deployed. The default choice when your SSO needs are serious. -Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -54,7 +54,7 @@ On the Keycloak side, **Admin Console → Users**: mark each RomM user's email a ## 5. Test -Restart RomM and open `/login`. Click **Login with Keycloak**; you're redirected to Keycloak, authenticate, and bounce back signed into RomM. +Restart RomM and open `/login`. Click **Login with Keycloak**. You're redirected to Keycloak, authenticate, and bounce back signed into RomM. If a user already exists in RomM with a matching email, they're signed into that account. Otherwise RomM creates a new account with Viewer permissions. diff --git a/docs/administration/oidc/pocketid.md b/docs/administration/oidc/pocketid.md index cde555c6..6db454e4 100644 --- a/docs/administration/oidc/pocketid.md +++ b/docs/administration/oidc/pocketid.md @@ -7,7 +7,7 @@ description: Wire RomM's SSO to PocketID for passkey-only login. [PocketID](https://github.com/stonith404/pocket-id) is a minimalist OIDC provider that **only** supports passkey authentication, with no passwords. Good fit when you want passwordless login end-to-end without the complexity of Keycloak or Authentik. -Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -22,7 +22,7 @@ In PocketID admin: 3. Fill in: - **Name**: `RomM` - **Callback URLs**: `https://romm.example.com/api/oauth/openid` -4. **Save**. Stay on this page; the client secret only displays **once**. Copy both the Client ID and Client Secret now. +4. **Save**. Stay on this page. The client secret only displays **once**. Copy both the Client ID and Client Secret now. ## 3. Configure RomM @@ -47,7 +47,7 @@ RomM → **Profile** → set your email to exactly the same address PocketID has ## 5. Test -Restart RomM and open `/login`. Click **Login with OIDC**; you're redirected to PocketID, tap your passkey, and bounce back signed into RomM. +Restart RomM and open `/login`. Click **Login with OIDC**. You're redirected to PocketID, tap your passkey, and bounce back signed into RomM. ![Login with OIDC](../../resources/pocketid/2-romm-login.png) diff --git a/docs/administration/oidc/zitadel.md b/docs/administration/oidc/zitadel.md index a39baabb..a19a96af 100644 --- a/docs/administration/oidc/zitadel.md +++ b/docs/administration/oidc/zitadel.md @@ -7,7 +7,7 @@ description: Wire RomM's SSO to Zitadel: project, application, user info inside [Zitadel](https://zitadel.com/) is an enterprise-grade open-source IAM platform supporting OAuth2, OIDC, SAML, and passwordless. Good fit when you want an enterprise-ish IdP without running Keycloak. -Before starting, read the [OIDC Setup overview](index.md); it covers the RomM-side settings common to every provider. +Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. ## 1. Prerequisites @@ -19,7 +19,7 @@ Create a new project (e.g. `RomM`). This holds the client and its auth settings. On the project's **General** tab, the toggles mean: -- **Assert Roles on Authentication**: not useful today; RomM 5.0 reads roles via `OIDC_CLAIM_ROLES` regardless. Leave off unless you're setting up role mapping. +- **Assert Roles on Authentication**: not useful today. RomM 5.0 reads roles via `OIDC_CLAIM_ROLES` regardless. Leave off unless you're setting up role mapping. - **Check authorization on Authentication**: recommended. If off, anyone who can register in Zitadel can sign into RomM (as Viewer). Turn this on if Zitadel registration is open. - **Check for Project on Authentication**: only matters if you're separating users by Zitadel organization. Skip for a single RomM instance. @@ -43,7 +43,7 @@ On the project's **General** tab, under **Applications**, click **New**. Tick ** - **Redirect URIs**: `https://romm.example.com/api/oauth/openid` - **Post Logout URIs**: `https://romm.example.com/` -Click **Create**. The **client secret is shown once**; copy it now. +Click **Create**. The **client secret is shown once**. Copy it now. ## 4. Enable claims in the ID Token @@ -74,6 +74,6 @@ In RomM → **Profile** → set your email to exactly the same address your Zita ## 7. Test -Restart RomM and open `/login`. Click **Login with Zitadel**; you're redirected, authenticate, and bounce back signed into RomM. +Restart RomM and open `/login`. Click **Login with Zitadel**. You're redirected, authenticate, and bounce back signed into RomM. If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index d1e0490a..811a93a5 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -15,7 +15,7 @@ All three share the same scan engine and the same set of **scan modes**. ## Scan modes -Every scan picks one mode. Modes differ in what they touch; use the most-targeted mode that accomplishes what you want. +Every scan picks one mode. Modes differ in what they touch, so use the most-targeted mode that accomplishes what you want. | Mode | What it does | When to use | | --- | --- | --- | @@ -32,13 +32,13 @@ You can further scope a scan to specific **platforms** and specific **metadata p **Scan button in the sidebar.** The Scan page shows: -- Platform checkboxes (select which to scan; leave empty to scan all). +- Platform checkboxes (select which to scan, leave empty to scan all). - Metadata provider toggles (overrides the default priority for this one scan). - Advanced options: skip hashing (helpful on low-power hosts), target a LaunchBox refresh. - A live log of everything the scanner is doing. - Per-platform progress panels with matched / unmatched / missing counts. -A running scan survives browser refreshes; the log streams over Socket.IO. Multiple admins opening the page see the same scan state. +A running scan survives browser refreshes, and the log streams over Socket.IO. Multiple admins opening the page see the same scan state. ## Scheduled scans @@ -55,7 +55,7 @@ To disable scheduled scans entirely, either unset the cron or set it to somethin ## Filesystem watcher -The watcher tails your library folder and schedules scans in response to file events: files added, moved, deleted. It's off by default on some deployments; enable with: +The watcher tails your library folder and schedules scans in response to file events: files added, moved, deleted. It's off by default on some deployments. Enable with: ```yaml environment: @@ -68,9 +68,9 @@ environment: Behaviour: - Watches `/romm/library` (and everything under it) recursively. -- Debounces bursts of events; the delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. +- Debounces bursts of events. The delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. - Batches scans intelligently: many events → a single consolidated scan, not one scan per file. -- Ignores content modifications and metadata-only changes; it cares about files appearing or disappearing, not about `chmod`. +- Ignores content modifications and metadata-only changes. It cares about files appearing or disappearing, not about `chmod`. - Skips OS noise (`.DS_Store`, `Thumbs.db`, `.tmp`, etc.). - If a whole new platform folder appears, switches to a **New Platforms** scan to pick it up cleanly. @@ -89,7 +89,7 @@ Behaviour: | Catches renames | Yes | Yes | | Survives a container restart | Yes, re-arms on startup | Yes | -Run both. The watcher handles day-to-day additions; the scheduled scan is a safety net. +Run both. The watcher handles day-to-day additions, and the scheduled scan is a safety net. ## What gets excluded diff --git a/docs/administration/scheduled-tasks.md b/docs/administration/scheduled-tasks.md index 91d95678..6e0d4e2b 100644 --- a/docs/administration/scheduled-tasks.md +++ b/docs/administration/scheduled-tasks.md @@ -33,7 +33,7 @@ Examples: - `*/30 * * * *`: every 30 minutes. - `0 2 * * 0`: 2 AM every Sunday. -Set the env var, restart the container. Alembic runs on every start; the scheduler picks up the new schedule the moment RomM comes back up. +Set the env var, restart the container. Alembic runs on every start, and the scheduler picks up the new schedule the moment RomM comes back up. ## Disabling a scheduled task @@ -87,11 +87,11 @@ Downloads an updated copy of the Nintendo Switch title ID database used for matc ### LaunchBox Metadata Sync -Refreshes the local LaunchBox metadata cache used when LaunchBox is enabled as a metadata provider. Nightly at 2 AM by default. Only useful if `LAUNCHBOX_API_ENABLED=true`; disable it otherwise to save a handful of CPU seconds. +Refreshes the local LaunchBox metadata cache used when LaunchBox is enabled as a metadata provider. Nightly at 2 AM by default. Only useful if `LAUNCHBOX_API_ENABLED=true`. Disable it otherwise to save a handful of CPU seconds. ### Image Conversion -Re-encodes fetched cover art, screenshots, and manuals to WebP for faster serving. Nightly at 3 AM. Idempotent; safe to run more often if you're importing a lot of media. +Re-encodes fetched cover art, screenshots, and manuals to WebP for faster serving. Nightly at 3 AM. Idempotent, so safe to run more often if you're importing a lot of media. ### RetroAchievements Sync @@ -111,16 +111,16 @@ Walks the DB, checks each ROM's file still exists on disk, drops entries whose f ### Cleanup Orphaned Resources (manual) -Deletes cover images, screenshots, and manuals no longer referenced by any ROM. Safe to run any time; can free tens of GB if you've been churning the library. +Deletes cover images, screenshots, and manuals no longer referenced by any ROM. Safe to run any time, and can free tens of GB if you've been churning the library. ## Monitoring tasks - **Live**: Administration → Tasks page shows every task's current status (queued, running, idle, failed). -- **API**: `GET /api/tasks/status` for a JSON summary; wire this to an uptime monitor if you want alerts. +- **API**: `GET /api/tasks/status` for a JSON summary. Wire this to an uptime monitor if you want alerts. - **Logs**: `docker logs romm` → look for `rq.worker` lines. -- **Heartbeat**: `GET /api/heartbeat` returns overall health plus per-task summary; handy for monitoring dashboards. +- **Heartbeat**: `GET /api/heartbeat` returns overall health plus per-task summary, handy for monitoring dashboards. -A task that's been "running" for hours is usually a scan that hit `SCAN_TIMEOUT_HOURS`; the log will say so. Tasks that fail leave a stack trace in the container logs; the RQ `failed` queue retains the last few for inspection. +A task that's been "running" for hours is usually a scan that hit `SCAN_TIMEOUT_HOURS`, and the log will say so. Tasks that fail leave a stack trace in the container logs, and the RQ `failed` queue retains the last few for inspection. ## Tuning for small hosts diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 58b561d4..64c68ace 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -27,7 +27,7 @@ A breakdown of disk usage by directory: | Bucket | Maps to | Grows when | | --- | --- | --- | | **Library** | `/romm/library` | You add ROMs. Controlled by you, not RomM. | -| **Resources** | `/romm/resources` | Scans fetch cover art, screenshots, manuals. Safe to purge; can be rebuilt from a rescan. | +| **Resources** | `/romm/resources` | Scans fetch cover art, screenshots, manuals. Safe to purge, can be rebuilt from a rescan. | | **Assets** | `/romm/assets` | Users upload saves, states, screenshots. **Back this up.** | | **Config** | `/romm/config` | Rarely: you or admins edit `config.yml`. | @@ -43,12 +43,12 @@ Under the summary, an expandable table sorted by either ROM count or disk usage. Useful for: "which platform is eating my disk?" and "which platform has the worst match rate?" !!! note "Per-platform stats are opt-in" - Computing per-platform stats walks the whole DB and costs real time on big libraries. The main stats load fast; per-platform expansion loads on demand. + Computing per-platform stats walks the whole DB and costs real time on big libraries. The main stats load fast, and per-platform expansion loads on demand. ## What the numbers don't include - **Disk usage for the database itself**: MariaDB / Postgres data volume isn't reported here. Use `docker system df -v` or your volume backend's native tooling. -- **Redis memory**: same; monitor Redis separately if you're tight on RAM. +- **Redis memory**: same. Monitor Redis separately if you're tight on RAM. - **Per-user storage breakdown**: not exposed in 5.0. ## Using stats for capacity planning @@ -57,11 +57,11 @@ Rule-of-thumb sizes: | Ratio | Typical value | | --- | --- | -| Resources / Library | 2-5% on average; higher if you've enabled many metadata providers or run Image Conversion often. | +| Resources / Library | 2-5% on average, higher if you've enabled many metadata providers or run Image Conversion often. | | Assets / Library | <1% unless you have lots of heavy PSP/PS3 saves. | | DB size / Games | ~10-50 KB per ROM row, depending on metadata richness. | -If **Resources** is ballooning, run the [Cleanup Orphaned Resources](scheduled-tasks.md#cleanup-orphaned-resources-manual) task. If **Assets** is growing unexpectedly, a user is probably uploading save states for long-form JRPGs; that's fine, just plan for it. +If **Resources** is ballooning, run the [Cleanup Orphaned Resources](scheduled-tasks.md#cleanup-orphaned-resources-manual) task. If **Assets** is growing unexpectedly, a user is probably uploading save states for long-form JRPGs. That's fine, just plan for it. ## API @@ -77,8 +77,8 @@ Wire to your monitoring stack via the API rather than scraping the HTML page. Se ## Troubleshooting -- **Numbers look stale**: stats are computed on page load, not cached. Reload. If still stale, the DB connection is degraded; check `docker logs romm 2>&1 | grep -i database`. +- **Numbers look stale**: stats are computed on page load, not cached. Reload. If still stale, the DB connection is degraded. Check `docker logs romm 2>&1 | grep -i database`. - **Disk sizes look wrong**: RomM reports what it sees in `/romm/*`. If your compose mounts a path that's smaller than the host dataset (e.g. you mounted a sub-directory), RomM only sees that subset. -- **"Platform stats couldn't load"**: the DB query timed out. On very large libraries this happens; retry, or raise `SCAN_TIMEOUT_HOURS` (yes, it also gates the stats query). +- **"Platform stats couldn't load"**: the DB query timed out. On very large libraries this happens. Retry, or raise `SCAN_TIMEOUT_HOURS` (yes, it also gates the stats query). For anything else: [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/administration/ssh-sync.md b/docs/administration/ssh-sync.md index 95e60eb3..bdeb557c 100644 --- a/docs/administration/ssh-sync.md +++ b/docs/administration/ssh-sync.md @@ -80,7 +80,7 @@ From the RomM container, confirm SSH works: docker exec romm ssh -i "$SSH_PRIVATE_KEY_PATH" user@ echo ok ``` -You should see `ok`. If you see a host-key prompt, accept it; RomM will remember it in its `known_hosts`. If you see `permission denied`, the authorised key isn't installed correctly. +You should see `ok`. If you see a host-key prompt, accept it. RomM will remember it in its `known_hosts`. If you see `permission denied`, the authorised key isn't installed correctly. ## How sync runs @@ -100,7 +100,7 @@ Disable sync for a specific device by deregistering it from **Administration → - **`Permission denied (publickey)`**: authorised key isn't set up on the device, or the private key inside the container can't be read (check the file permissions and bind-mount flags). - **`Host key verification failed`**: the device's host key changed (after a reinstall, typically). Shell into the container and remove the offending line from `~/.ssh/known_hosts`. -- **Sync silently doesn't run**: check `GET /api/tasks/status` for the Push-Pull task's state. "failed" points you at the error; "never ran" means the cron isn't firing (see [Scheduled Tasks](scheduled-tasks.md)). +- **Sync silently doesn't run**: check `GET /api/tasks/status` for the Push-Pull task's state. "failed" points you at the error, and "never ran" means the cron isn't firing (see [Scheduled Tasks](scheduled-tasks.md)). - **Connection times out**: the device is offline or the network path is blocked. Confirm reachability from the RomM container: `docker exec romm ping `. More at [Device Sync Troubleshooting](../troubleshooting/sync.md). diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index 62508b65..6ef8197d 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -5,7 +5,7 @@ description: User management, roles, and the scope model in RomM 5.0. # Users & Roles -RomM is multi-user from the start. The first user created during Setup is always an **Admin**; everyone after that gets the role you assign when creating the account. +RomM is multi-user from the start. The first user created during Setup is always an **Admin**, and everyone after that gets the role you assign when creating the account. ## Roles @@ -15,11 +15,11 @@ RomM is multi-user from the start. The first user created during Setup is always | **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | | **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | -Roles are a convenience layer on top of **scopes**; see the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0; if you need finer-grained access, use the role that's most restrictive and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. +Roles are a convenience layer on top of **scopes**. See the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0. If you need finer-grained access, use the role that's most restrictive and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. ## Scope matrix -RomM authorisation is scope-based. Every API call and UI action maps to one or more scopes; OAuth tokens and OIDC sessions carry a subset of them. Nineteen scopes total, grouped by resource: +RomM authorisation is scope-based. Every API call and UI action maps to one or more scopes, and OAuth tokens and OIDC sessions carry a subset of them. Nineteen scopes total, grouped by resource: | Scope | Purpose | Viewer | Editor | Admin | | --- | --- | :---: | :---: | :---: | @@ -55,9 +55,9 @@ Two ways: Better when you don't want to handle someone else's password. -1. **Administration → Users → Invite.** Pick a role; RomM generates a single-use invite link. +1. **Administration → Users → Invite.** Pick a role, and RomM generates a single-use invite link. 2. Send the link. The recipient opens it, picks their own username and password, and is logged in. -3. Invite links expire; the default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). +3. Invite links expire. The default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). ### Public self-registration @@ -65,7 +65,7 @@ Off by default. To let anyone with the URL register their own Viewer account, se ### OIDC -If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md); look for `OIDC_CLAIM_ROLES` and the per-role env vars. +If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md). Look for `OIDC_CLAIM_ROLES` and the per-role env vars. ## Editing and deleting users @@ -77,4 +77,4 @@ Deleting a user keeps their contributions (collections they made public, ROM met ## API tokens (advanced) -Each user can issue up to 25 **Client API Tokens** from **Administration → Client API Tokens**. Tokens carry a subset of the user's scopes and are the right way to authenticate companion apps (Argosy, Grout, Playnite, custom scripts). The pairing flow for devices is covered in [Client API Tokens](../ecosystem/client-api-tokens.md); the API side is in [API Authentication](../developers/api-authentication.md). +Each user can issue up to 25 **Client API Tokens** from **Administration → Client API Tokens**. Tokens carry a subset of the user's scopes and are the right way to authenticate companion apps (Argosy, Grout, Playnite, custom scripts). The pairing flow for devices is covered in [Client API Tokens](../ecosystem/client-api-tokens.md), and the API side is in [API Authentication](../developers/api-authentication.md). diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index ff5401fb..845ec041 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -1,6 +1,6 @@ --- title: API Authentication -description: How to authenticate to the RomM REST API: session cookies, Basic, OAuth2 tokens, client tokens, and OIDC. +description: How to authenticate to the RomM REST API. Session cookies, Basic, OAuth2 tokens, client tokens, and OIDC. --- # API Authentication @@ -15,7 +15,7 @@ RomM's REST API accepts four authentication modes. Pick the one that matches you | **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | | **OIDC session** | Users who sign in via SSO | Same as session cookie, but issued after OIDC callback | -All of them resolve to the same scope model; see the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. +All of them resolve to the same scope model. See the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. ## Base URL @@ -46,7 +46,7 @@ For OIDC logins, hitting `/api/auth/logout` also triggers RP-Initiated Logout if ## HTTP Basic -Fine for quick scripts. Avoid in shared environments; the credentials are sent on every request. +Fine for quick scripts. Avoid in shared environments, because the credentials are sent on every request. ```bash curl -u alice:s3cret https://romm.example.com/api/roms @@ -94,7 +94,7 @@ Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=eyJhbGciOi... ``` -Request only the scopes you need; RomM will issue a token with the intersection of what you asked for and what the user has. +Request only the scopes you need. RomM will issue a token with the intersection of what you asked for and what the user has. ## Client API tokens (for companion apps) @@ -123,7 +123,7 @@ Every endpoint in the [API Reference](api-reference.md) lists its required scope - **Write**-ish endpoints want `*.write`. - **Admin** endpoints (anything under `/api/users` beyond `me`, `/api/tasks/run`) want `users.read`, `users.write`, or `tasks.run`. -A token that holds `users.write` also implicitly grants lesser scopes like `users.read` or `me.read`; RomM doesn't require you to list them all. But a token that holds only `roms.read` can't write, even if the underlying user is an Admin. +A token that holds `users.write` also implicitly grants lesser scopes like `users.read` or `me.read`, so RomM doesn't require you to list them all. But a token that holds only `roms.read` can't write, even if the underlying user is an Admin. ## Errors @@ -136,7 +136,7 @@ A token that holds `users.write` also implicitly grants lesser scopes like `user When debugging a 403, check: 1. The **user's role** in Administration → Users. -2. The **token's scopes** (for OAuth2/Client API Tokens); scopes are narrower than the user's role by default. +2. The **token's scopes** (for OAuth2/Client API Tokens). Scopes are narrower than the user's role by default. 3. The endpoint's scope requirements in the [API Reference](api-reference.md). ## OpenAPI diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md index 08154f99..784b91ab 100644 --- a/docs/developers/api-reference.md +++ b/docs/developers/api-reference.md @@ -1,11 +1,11 @@ --- title: API Reference -description: Catalogue of RomM's REST API; authoritative interactive docs live on each instance. +description: Catalogue of RomM's REST API. Authoritative interactive docs live on each instance. --- # API Reference -RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth; this page catalogues what's there, but every running RomM instance serves its own interactive browser with live "try it out" functionality. +RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth. This page catalogues what's there, but every running RomM instance serves its own interactive browser with live "try it out" functionality. ## Interactive docs @@ -42,7 +42,7 @@ Full walkthrough in [API Authentication](api-authentication.md). ## Endpoint groups -Every endpoint belongs to a group. Summaries below; the interactive docs at `/api/docs` have full request/response schemas. +Every endpoint belongs to a group. Summaries below. The interactive docs at `/api/docs` have full request/response schemas. ### Auth & users diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index eb1781e8..40c37a66 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -66,7 +66,7 @@ A running RomM container hosts several cooperating processes: ### The parts - **nginx:** listens on `8080`. Serves static assets (the SPA bundle, EmulatorJS core files, Ruffle, cover images). Proxies API + WebSocket traffic to gunicorn. Streams large downloads via `mod_zip`. -- **gunicorn:** the Python WSGI server, running the FastAPI app. Multiple worker processes; `WEB_SERVER_CONCURRENCY` tunes count. +- **gunicorn:** the Python WSGI server, running the FastAPI app. Multiple worker processes. `WEB_SERVER_CONCURRENCY` tunes count. - **FastAPI backend:** routes, handlers, DB access via SQLAlchemy, Redis for sessions + cache + queue. - **RQ workers:** separate process(es) that pop jobs off Redis queues and run them. Scans, metadata syncs, cleanup tasks. - **Redis / Valkey:** in-container by default (full image), externalisable. @@ -135,13 +135,13 @@ RQ has three priority queues: - **default:** scheduled nightlies, sync operations. - **low:** cleanup, image conversion. Run when the system's idle. -All queues share the same worker pool. Jobs are Redis-backed, so restarting RomM doesn't lose in-flight work (appendonly needs to be on; see [Redis or Valkey](../install/redis-or-valkey.md)). +All queues share the same worker pool. Jobs are Redis-backed, so restarting RomM doesn't lose in-flight work (appendonly needs to be on, see [Redis or Valkey](../install/redis-or-valkey.md)). ## Auth Session state in Redis. Passwords bcrypt-hashed in the DB. JWT for OAuth2 bearer tokens. -OIDC handled via `authlib` on the backend. Session cookies issued after successful OIDC callback; from there, it's a regular session. +OIDC handled via `authlib` on the backend. Session cookies issued after successful OIDC callback. From there, it's a regular session. Client API Tokens are stored as hash-only in the DB (we never store the plaintext token after creation). A token is validated by hashing the presented bearer and comparing. @@ -165,7 +165,7 @@ See [Observability](../administration/observability.md). See [Contributing](contributing.md) for the process + style expectations. -For large changes, read the relevant handler in `backend/handlers/` first; the patterns there will guide what you write. +For large changes, read the relevant handler in `backend/handlers/` first. The patterns there will guide what you write. ## See also diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index 8e71100d..2c9db9ac 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -17,7 +17,7 @@ The project follows the [Contributor Covenant](https://github.com/rommapp/romm/b ## AI assistance: please disclose !!! warning "Required disclosure" - If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR, including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count; anything more does. If PR responses are generated by an AI, disclose that too. + If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR, including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count, but anything more does. If PR responses are generated by an AI, disclose that too. Example disclosures: @@ -27,7 +27,7 @@ Or more granularly: > I consulted ChatGPT to understand the codebase, but the solution was fully authored manually. -This isn't about gatekeeping AI contributions; it's about letting reviewers know how much scrutiny to apply. AI-assisted code often ranges from "fantastic" to "hallucinated nonsense"; we need to calibrate. +This isn't about gatekeeping AI contributions. It's about letting reviewers know how much scrutiny to apply. AI-assisted code often ranges from "fantastic" to "hallucinated nonsense", so we need to calibrate. Failing to disclose is rude to the humans reviewing your PR. Don't do it. @@ -57,7 +57,7 @@ uv run mkdocs serve ### Translations -Create a new folder under `frontend/src/locales/` using the existing language files as a template, translate the strings, and open a PR. Partial translations are welcome; we'd rather have an 80%-translated locale than nothing. +Create a new folder under `frontend/src/locales/` using the existing language files as a template, translate the strings, and open a PR. Partial translations are welcome. We'd rather have an 80%-translated locale than nothing. See [Translations (i18n)](i18n.md) for the full translator workflow. @@ -68,7 +68,7 @@ See [Translations (i18n)](i18n.md) for the full translator workflow. - What happened, what you expected. - Exact reproduction steps. - RomM version and how you deployed it (Docker tag, Unraid, K8s, etc.). -- Relevant logs (`docker logs romm`; redact any secrets). +- Relevant logs (`docker logs romm`, redact any secrets). The bug report template prompts for all of this. @@ -100,7 +100,7 @@ The bug report template prompts for all of this. ## PR guidelines -- **Lint clean.** `trunk check` must pass; CI will block otherwise. See [Development Setup → Linting](development-setup.md#linting). +- **Lint clean.** `trunk check` must pass, or CI will block otherwise. See [Development Setup → Linting](development-setup.md#linting). - **Tests pass + new tests for new code.** `uv run pytest` must pass. New behaviour needs coverage. - **Docs updated** when behaviour changes. Even a one-line update to the relevant docs page is better than stale docs. - **Clear title + description.** "Fix bug" isn't a title. "Fix scan skipping multi-disc PS1 games when first disc is a .chd" is. diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index 924e13fe..9fa5743a 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -76,11 +76,11 @@ cp env.template .env sudo apt install libmariadb3 libmariadb-dev libpq-dev ``` -Adjust for your distro; the key libraries are the MariaDB connector and libpq for Postgres. +Adjust for your distro. The key libraries are the MariaDB connector and libpq for Postgres. #### RAHasher (optional) -Only needed if you're working on RetroAchievements hash calculation. **Not supported on macOS; skip.** +Only needed if you're working on RetroAchievements hash calculation. **Not supported on macOS, skip.** ```sh git clone --recursive https://github.com/RetroAchievements/RALibretro.git @@ -142,11 +142,11 @@ ln -s ../../romm_mock/assets assets/romm/assets npm run dev ``` -Frontend is on `http://localhost:3000`; it proxies API calls through to the backend. +Frontend is on `http://localhost:3000`, and it proxies API calls through to the backend. ## Linting -RomM uses [Trunk](https://trunk.io) as a meta-linter; it wraps ruff, prettier, eslint, markdownlint, and a few others under one config. +RomM uses [Trunk](https://trunk.io) as a meta-linter. It wraps ruff, prettier, eslint, markdownlint, and a few others under one config. ```sh curl https://get.trunk.io -fsSL | bash @@ -158,7 +158,7 @@ trunk check # report what it can't Trunk runs as a pre-commit hook automatically after install. Alternative install methods are in [the Trunk docs](https://docs.trunk.io/check/usage#install-the-cli). !!! warning "CI blocks un-linted PRs" - Trunk's check runs on every PR. If it fails, your PR can't merge; same rules as the maintainers. + Trunk's check runs on every PR. If it fails, your PR can't merge. Same rules as the maintainers. ## Tests diff --git a/docs/developers/i18n.md b/docs/developers/i18n.md index 5aaf8f61..1048e451 100644 --- a/docs/developers/i18n.md +++ b/docs/developers/i18n.md @@ -13,7 +13,7 @@ This page is for **contributors** adding or improving translations. - **App UI** is translated. 19 locales currently ship. - **Docs** (what you're reading) are English-only in 5.0. -- **Game metadata** isn't translated by RomM; that comes from metadata providers (IGDB, ScreenScraper). Their localisation varies per title. +- **Game metadata** isn't translated by RomM. That comes from metadata providers (IGDB, ScreenScraper). Their localisation varies per title. ## Tech stack @@ -68,12 +68,12 @@ Scenario: the language you want isn't in the dropdown yet. 1. Pick your locale code. Use [BCP 47](https://en.wikipedia.org/wiki/IETF_language_tag) format: `de_DE`, `pt_BR`, `zh_TW`. The underscore convention matches existing folders. 2. Copy `frontend/src/locales/en_US/` to `frontend/src/locales//`. -3. Translate the strings. Start with the most-visible ones (menu, navigation, home dashboard); partial coverage is welcome. -4. Register the locale in the index file (filename varies; look for something like `frontend/src/locales/index.ts` that lists active locales). +3. Translate the strings. Start with the most-visible ones (menu, navigation, home dashboard). Partial coverage is welcome. +4. Register the locale in the index file (filename varies, look for something like `frontend/src/locales/index.ts` that lists active locales). 5. Run `npm run dev` and verify your locale appears in the Language dropdown. 6. Open a PR. -**Partial translations are merged.** Don't hold out for 100%; English fallback handles gaps gracefully. A 60%-translated Arabic is better than no Arabic. +**Partial translations are merged.** Don't hold out for 100%, because English fallback handles gaps gracefully. A 60%-translated Arabic is better than no Arabic. ## Style guidance @@ -81,13 +81,13 @@ Scenario: the language you want isn't in the dropdown yet. Match the source tone: -- **Conversational but not too casual.** "Let's scan your library" is fine; "Yo, scan your roms dude" isn't. +- **Conversational but not too casual.** "Let's scan your library" is fine, but "Yo, scan your roms dude" isn't. - **Active voice.** "Scan the library", not "The library is being scanned". - **Consistent formality.** Pick `tu`/`vous`, formal/informal `you`, etc., and stick with it across the locale. ### Consistency -Use the same word for the same concept throughout. If you translate "Collection" as one word at the top of a file, don't translate it differently three screens later. The `en_US` file has a glossary at the top for key terms; mirror that pattern in your locale. +Use the same word for the same concept throughout. If you translate "Collection" as one word at the top of a file, don't translate it differently three screens later. The `en_US` file has a glossary at the top for key terms. Mirror that pattern in your locale. ### Interpolations @@ -108,7 +108,7 @@ Pluralisation: } ``` -Pipe-separated forms: zero | one | other. Not every language has three; `vue-i18n`'s pluralisation docs explain per-language rules. +Pipe-separated forms: zero | one | other. Not every language has three, and `vue-i18n`'s pluralisation docs explain per-language rules. ### What not to translate @@ -128,19 +128,19 @@ No RTL locales ship yet. The UI needs layout work before Arabic / Hebrew can pre Look for: -- **Clipped text.** Some translations are longer than their English originals; if a button breaks layout, flag it. +- **Clipped text.** Some translations are longer than their English originals. If a button breaks layout, flag it. - **Grammatical weirdness with variables.** "Found 1 games" is wrong. Fix with pluralisation. - **Fallback to English.** Shows up when a key is missing. Add it. ## Tooling -We don't use a translation-management platform (Weblate, Crowdin, etc.) in 5.0; translations happen via PR. If interest grows, we may add one. Open an issue to discuss if you'd prefer it. +We don't use a translation-management platform (Weblate, Crowdin, etc.) in 5.0. Translations happen via PR. If interest grows, we may add one. Open an issue to discuss if you'd prefer it. ## PR conventions - One PR per locale is ideal. - Title: `i18n(): `. E.g. `i18n(fr_FR): fill in missing Admin panel strings`. -- Include screenshots of the changed screens; reviewers can't read every locale. +- Include screenshots of the changed screens, because reviewers can't read every locale. - Disclose AI assistance per [Contributing](contributing.md). ## See also diff --git a/docs/developers/index.md b/docs/developers/index.md index 028e7486..8c9d5a87 100644 --- a/docs/developers/index.md +++ b/docs/developers/index.md @@ -28,7 +28,7 @@ Looking for end-user or operator content? See [Using RomM](../using/index.md) or - **[Architecture](architecture.md):** high-level walkthrough of the codebase. - **[Contributing](contributing.md):** process, style, AI-assistance disclosure. - **[Translations (i18n)](i18n.md):** add or improve a locale. -- **[Releasing](releasing.md):** maintainer-only; how releases are cut. +- **[Releasing](releasing.md):** maintainer-only, how releases are cut. ## Reference diff --git a/docs/developers/openapi.md b/docs/developers/openapi.md index 6280aee4..3e50000e 100644 --- a/docs/developers/openapi.md +++ b/docs/developers/openapi.md @@ -22,7 +22,7 @@ Human-readable versions: - **Swagger UI** at `{romm_url}/api/docs` - **ReDoc** at `{romm_url}/api/redoc` -Both are auto-generated from the same `openapi.json`. Swagger UI has a "Try it out" feature for live testing; ReDoc has a cleaner reading layout. +Both are auto-generated from the same `openapi.json`. Swagger UI has a "Try it out" feature for live testing, and ReDoc has a cleaner reading layout. ## Versioning @@ -56,7 +56,7 @@ npx @openapitools/openapi-generator-cli generate \ -o ./romm-client-ts ``` -Generated clients handle auth, request shaping, and response parsing. Drop in, import, call. Quality varies by generator target; Python and TypeScript are the best-tested. +Generated clients handle auth, request shaping, and response parsing. Drop in, import, call. Quality varies by generator target, but Python and TypeScript are the best-tested. ### Tips @@ -86,7 +86,7 @@ If you're building something that calls RomM, consider validating requests again A few known quirks to work around: -- **Some `additionalProperties` are loose.** RomM's spec lets some responses include fields not in the schema (debug hooks, feature-flag-gated fields). Don't treat the spec as an exact response guarantee; treat it as "everything here is always present; more may follow". +- **Some `additionalProperties` are loose.** RomM's spec lets some responses include fields not in the schema (debug hooks, feature-flag-gated fields). Don't treat the spec as an exact response guarantee. Treat it as "everything here is always present, more may follow". - **Socket.IO isn't in the OpenAPI spec.** WebSocket endpoints are documented separately in [WebSockets](websockets.md). - **Pagination defaults vary per endpoint.** Some paginate, some don't. Check the spec per endpoint. diff --git a/docs/developers/releasing.md b/docs/developers/releasing.md index 05f06b33..1a769384 100644 --- a/docs/developers/releasing.md +++ b/docs/developers/releasing.md @@ -11,7 +11,7 @@ Maintainer reference. If you're not cutting a RomM release, you don't need this RomM releases on a loose cadence, not scheduled, driven by readiness: -- **Patch (`5.0.1`, `5.0.2`):** bug fixes. Cut as needed; typically 1-4 per month. +- **Patch (`5.0.1`, `5.0.2`):** bug fixes. Cut as needed, typically 1-4 per month. - **Minor (`5.1.0`, `5.2.0`):** additive features. Cut when a cohesive batch of features is stable. - **Major (`6.0.0`):** breaking changes. Planned well in advance, announced in the Discord + on GitHub. @@ -23,7 +23,7 @@ RomM releases on a loose cadence, not scheduled, driven by readiness: - **MINOR:** new feature, backwards-compatible. - **PATCH:** bug fix only. -Alembic migrations run on every startup; migrations are backwards-compatible within a major version. +Alembic migrations run on every startup, and migrations are backwards-compatible within a major version. ## Pre-release checklist @@ -38,13 +38,13 @@ Alembic migrations run on every startup; migrations are backwards-compatible wit - `pyproject.toml` → `version = "X.Y.Z"`. - `frontend/package.json` → `"version": "X.Y.Z"`. -- Any hardcoded version strings (`backend/__init__.py`, etc.); `rg '__version__'` or `rg '5\.0\.0'` to find them. +- Any hardcoded version strings (`backend/__init__.py`, etc.). `rg '__version__'` or `rg '5\.0\.0'` to find them. ### 3. Update `env.template` if needed If the release adds / renames / removes env vars, `env.template` is the canonical reference. Add/rename/remove lines with inline comments. Keep alphabetical order per section. -The docs site auto-generates [Environment Variables](../reference/environment-variables.md) from `env.template` on every release; skip this step and the docs drift. +The docs site auto-generates [Environment Variables](../reference/environment-variables.md) from `env.template` on every release. Skip this step and the docs drift. ### 4. Update changelog @@ -106,7 +106,7 @@ docker push rommapp/romm:latest docker push rommapp/romm:5 ``` -(Normally automated in the release workflow; manual fallback above.) +(Normally automated in the release workflow, manual fallback above.) ## Announcements @@ -140,7 +140,7 @@ If a regression ships in the release: ### Track issues -Post-release, expect a spike in issues. Triage Day-1 issues aggressively: breakage reports need immediate attention; nice-to-haves can wait. +Post-release, expect a spike in issues. Triage Day-1 issues aggressively: breakage reports need immediate attention, and nice-to-haves can wait. ## Security releases diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md index bc5795a3..13c00224 100644 --- a/docs/developers/websockets.md +++ b/docs/developers/websockets.md @@ -18,7 +18,7 @@ Both are Redis-backed (via `socket.io-redis`) so multi-instance RomM deployments RomM inherited Socket.IO from the Vue frontend, which uses `socket.io-client`. Sticking with Socket.IO avoids protocol drift, works in every browser, and handles reconnection + message framing for us. -If you're writing a non-browser client in a language that has a Socket.IO library (Python's `python-socketio`, Go's `go-socket.io`, etc.), the protocol is straightforward. Raw WebSocket without Socket.IO framing **will not** work; Socket.IO adds its own handshake and message envelope. +If you're writing a non-browser client in a language that has a Socket.IO library (Python's `python-socketio`, Go's `go-socket.io`, etc.), the protocol is straightforward. Raw WebSocket without Socket.IO framing **will not** work, because Socket.IO adds its own handshake and message envelope. ## Authentication @@ -58,16 +58,16 @@ Default namespace. No sub-namespacing in 5.0. ### Client → server events -No state changes via WebSocket; RomM's design is "REST for writes, Socket.IO for reads". A client can: +No state changes via WebSocket. RomM's design is "REST for writes, Socket.IO for reads". A client can: - Emit `subscribe:scan` with a scan ID to join that scan's broadcast group. - Emit `unsubscribe:scan` to leave. -Actual scan / task / ROM operations happen via the REST API; see [API Reference](api-reference.md). +Actual scan / task / ROM operations happen via the REST API. See [API Reference](api-reference.md). ## `/netplay/socket.io`: Netplay coordination -Separate endpoint for Netplay rooms. Used by EmulatorJS's Netplay logic; rarely touched directly. +Separate endpoint for Netplay rooms. Used by EmulatorJS's Netplay logic, rarely touched directly. ### Server → client events @@ -100,7 +100,7 @@ Without sticky sessions, Socket.IO's handshake polling phase can bounce between ## Reverse-proxy requirements -Every reverse-proxy setup must forward the WebSocket upgrade. See [Reverse Proxy](../install/reverse-proxy.md); all recipes there keep WebSockets on. +Every reverse-proxy setup must forward the WebSocket upgrade. See [Reverse Proxy](../install/reverse-proxy.md). All recipes there keep WebSockets on. Common breakages: @@ -140,7 +140,7 @@ sio.wait() - **Not every backend action emits a WS event.** Only the ones listed above. If you need a specific event, open an issue. - **No room-based user presence yet.** "Who else is online" isn't exposed. -- **Netplay WebRTC is peer-to-peer after initial handshake.** RomM only brokers; the actual gameplay data never touches RomM's servers. +- **Netplay WebRTC is peer-to-peer after initial handshake.** RomM only brokers. The actual gameplay data never touches RomM's servers. ## See also diff --git a/docs/ecosystem/argosy.md b/docs/ecosystem/argosy.md index b1b81b74..607a4216 100644 --- a/docs/ecosystem/argosy.md +++ b/docs/ecosystem/argosy.md @@ -82,10 +82,10 @@ No other permissions. The app doesn't request contacts, camera, location, or any ## Troubleshooting -- **Can't connect to RomM.** Check the URL (including `https://`), and that the RomM instance is reachable from your mobile network. Cellular might be blocked; try Wi-Fi first. +- **Can't connect to RomM.** Check the URL (including `https://`), and that the RomM instance is reachable from your mobile network. Cellular might be blocked, so try Wi-Fi first. - **Token invalid.** Pair again. Tokens can expire or be revoked on the RomM side. - **Emulator won't launch.** Make sure the emulator app is installed and Argosy has permission to open it. Some emulators require an intent-filter setup. -- **Downloads fail partway.** Usually network; Argosy resumes on retry. +- **Downloads fail partway.** Usually network, and Argosy resumes on retry. Full sync-specific debugging in [Device Sync Troubleshooting](../troubleshooting/sync.md). diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index edac9c21..119dd8b6 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -11,7 +11,7 @@ Tokens are **per-user** and **per-scope-subset**: a token can hold any subset of ## Why not just store a password? -- Passwords grant full access to the account; tokens can be scope-narrowed. +- Passwords grant full access to the account, but tokens can be scope-narrowed. - Tokens are one-click revocable without changing your password. - Tokens are safer to type (or paste) into a companion app's config file than a password. - Tokens can be bound to a single device via the pairing flow, avoiding typing them at all. @@ -35,10 +35,10 @@ Authorization: Bearer rmm_abcdef... From the RomM UI: **Profile → Client API Tokens → + New Token**. - **Name**: descriptive (e.g. "Grout on RG35XX"). -- **Scopes**: tick which scopes to include. Default: read-only. Think about it; don't give every token `users.write`. -- **Expiry**: optional; blank = never expires until revoked. +- **Scopes**: tick which scopes to include. Default: read-only. Think about it, and don't give every token `users.write`. +- **Expiry**: optional, blank = never expires until revoked. -The token is shown **exactly once**. Copy it now. If you lose it, revoke and regenerate; you can't get it back. +The token is shown **exactly once**. Copy it now. If you lose it, revoke and regenerate, because you can't get it back. ## Device pairing (short-code flow) @@ -77,7 +77,7 @@ Typing a 44-character token into a handheld thumbstick isn't realistic. Instead: ### Who generates the code -The user who owns the token, from a device already signed into RomM (web UI, usually). The handheld / companion device then enters the code. No way for a device to generate a code on its own; that would defeat the pairing. +The user who owns the token, from a device already signed into RomM (web UI, usually). The handheld / companion device then enters the code. No way for a device to generate a code on its own, because that would defeat the pairing. ### What "pairing" gives you @@ -161,7 +161,7 @@ The UI's scope-selection step defaults to read-only. Only tick write scopes you If the owning user's role drops below what the token needs: - Token continues to exist but fails at request time with **403 Forbidden**. -- RomM doesn't automatically revoke it; that's the user's decision. +- RomM doesn't automatically revoke it. That's the user's decision. If the user is deleted, all their tokens are revoked immediately. @@ -169,7 +169,7 @@ If the user is deleted, all their tokens are revoked immediately. - **Sharing a token between users.** Tokens are single-user. If two people need access, give them each an account and each creates their own token. - **Embedding a token in public source.** Obvious but worth saying. If you accidentally commit one, revoke immediately from the RomM UI. -- **A single token for every app.** Name and scope per-app; revoking one doesn't kill the others. +- **A single token for every app.** Name and scope per-app, so revoking one doesn't kill the others. - **Infinite-expiry tokens in untrusted locations.** If a device might be lost / handed off, set an expiry. ## See also diff --git a/docs/ecosystem/community-apps.md b/docs/ecosystem/community-apps.md index f44209df..9ddaaafb 100644 --- a/docs/ecosystem/community-apps.md +++ b/docs/ecosystem/community-apps.md @@ -5,7 +5,7 @@ description: Third-party companion apps for RomM, maintained by the community, n # Community Apps -Apps listed here are **community-maintained**. The RomM team doesn't build or officially support them; the authors do. Support is via the individual app's issue tracker and the RomM Discord. +Apps listed here are **community-maintained**. The RomM team doesn't build or officially support them. The authors do. Support is via the individual app's issue tracker and the RomM Discord. First-party alternatives (built by the RomM team): @@ -78,7 +78,7 @@ SteamOS downloader/syncer for Steam Deck. ### SwitchRomM -Homebrew NRO app for Nintendo Switch; pull ROMs from RomM over Wi-Fi. +Homebrew NRO app for Nintendo Switch. Pull ROMs from RomM over Wi-Fi. - **Author:** [@Shalasere](https://github.com/Shalasere) - **Platform:** Nintendo Switch (homebrew) @@ -113,7 +113,7 @@ Push a Syncthing-managed library to RomM automatically. ## "Community-maintained": what it means - **The RomM team doesn't build these.** We won't fix bugs, ship features, or respond to support tickets for community apps. -- **Support through the app author.** Each project has its own issue tracker; use it. +- **Support through the app author.** Each project has its own issue tracker. Use it. - **Install at your own risk.** We don't code-review community apps or vouch for their security posture. - **Token safety.** These apps use [Client API Tokens](client-api-tokens.md) the same way first-party apps do. Scope tokens narrowly and revoke if an app misbehaves. diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md index fd37bb3a..91b6fc82 100644 --- a/docs/ecosystem/device-sync-protocol.md +++ b/docs/ecosystem/device-sync-protocol.md @@ -71,8 +71,8 @@ The device remembers `id` for all subsequent calls. | Mode | Behaviour | | --- | --- | -| `pull_only` | Server only pushes; device-side changes are ignored. | -| `push_only` | Device pushes saves up; server changes never flow down. | +| `pull_only` | Server only pushes, and device-side changes are ignored. | +| `push_only` | Device pushes saves up, and server changes never flow down. | | `push_pull` | Bidirectional (most common). | ## Sync negotiation @@ -151,7 +151,7 @@ RomM returns a set of **operations** the device should execute: - `keep_both`: rename and keep both copies. - `server_wins` / `device_wins`: overwrite the other. - Default is `keep_both`. -- **`noop`**: nothing to do; hashes match. +- **`noop`**: nothing to do because hashes match. ### Execution @@ -230,12 +230,12 @@ Companion apps generally prefer the API model. SSH-based sync exists mostly for Not strict in 5.0. Reasonable rule of thumb: - Sync once per session (not every save). -- Large bursts (initial sync of a full library) are fine; RomM handles them. -- Don't poll `/api/sync/negotiate` in a tight loop; it's expensive server-side. +- Large bursts (initial sync of a full library) are fine, and RomM handles them. +- Don't poll `/api/sync/negotiate` in a tight loop, because it's expensive server-side. ## Event notifications -Currently polling-only. Companion apps check `/api/sync/negotiate` periodically (Grout defaults to every 15 minutes on Wi-Fi). Future versions may add a push notification channel; until then, polling. +Currently polling-only. Companion apps check `/api/sync/negotiate` periodically (Grout defaults to every 15 minutes on Wi-Fi). Future versions may add a push notification channel. Until then, polling. ## See also diff --git a/docs/ecosystem/fpkgi.md b/docs/ecosystem/fpkgi.md index bb539c3a..9f5d1b05 100644 --- a/docs/ecosystem/fpkgi.md +++ b/docs/ecosystem/fpkgi.md @@ -7,11 +7,11 @@ description: Install PS4 / PS5 packages from your RomM library via fpkgi homebre [fpkgi](https://github.com/CyberYoshi64/fpkgi) is PS4 / PS5 homebrew for installing `.pkg` packages from custom URL feeds. RomM exposes fpkgi-compatible feeds for its PS4 and PS5 libraries. -New in RomM 5.0; earlier versions didn't have fpkgi feeds. +New in RomM 5.0. Earlier versions didn't have fpkgi feeds. ## Prerequisites -- **PS4 or PS5** with fpkgi installed (requires CFW / jailbreak; setup is out of scope here). +- **PS4 or PS5** with fpkgi installed (requires CFW / jailbreak, and setup is out of scope here). - **RomM reachable from the console over Wi-Fi.** LAN simplest. - Games stored as `.pkg` files. fpkgi, like pkgj, only handles the Sony installer format. @@ -38,11 +38,11 @@ The feed returns JSON in the fpkgi-expected schema: titles, title IDs, content t Exact steps depend on the fpkgi version, but the gist: -1. Put RomM's feed URL in fpkgi's config (usually a JSON file on the console; check fpkgi's own docs). +1. Put RomM's feed URL in fpkgi's config (usually a JSON file on the console, so check fpkgi's own docs). 2. Restart fpkgi. 3. The RomM library appears in fpkgi's browse view. -Consult [fpkgi's README](https://github.com/CyberYoshi64/fpkgi) for the current config-file location and format; the project moves faster than these docs. +Consult [fpkgi's README](https://github.com/CyberYoshi64/fpkgi) for the current config-file location and format. The project moves faster than these docs. ## Authentication diff --git a/docs/ecosystem/grout.md b/docs/ecosystem/grout.md index faa6abf4..29492963 100644 --- a/docs/ecosystem/grout.md +++ b/docs/ecosystem/grout.md @@ -18,14 +18,14 @@ description: Official Linux handheld companion for muOS and NextUI, sync ROMs an - **Pulls** ROMs from RomM to the handheld's SD card, organised into muOS / NextUI's expected folder layout. - **Pushes** saves and states back to RomM when you finish a session. - **Schedules** sync runs: on idle, on session end, or on a cron. -- Works fully offline between syncs; the handheld doesn't need RomM to play. +- Works fully offline between syncs, so the handheld doesn't need RomM to play. ## Why Grout (and not Argosy)? - **[Argosy](argosy.md)** is Android-native. Good for Android handhelds (Retroid Pocket running Android, phones). - **Grout** is for non-Android Linux handhelds. muOS and NextUI are Linux, not Android. Argosy's APK won't install. -Same underlying protocol; different client for different OS. +Same underlying protocol, but different client for different OS. ## Installing @@ -40,7 +40,7 @@ Same underlying protocol; different client for different OS. ### On NextUI 1. Download the NextUI-flavoured release from the same releases page. -2. Follow NextUI's standard app install flow (paths differ by device; see NextUI docs). +2. Follow NextUI's standard app install flow (paths differ by device, see NextUI docs). ## First-time setup @@ -68,9 +68,9 @@ Bulk select for multi-file downloads, useful for pulling a whole collection at o Grout → Settings → Sync: -- **Pull** cadence: how often to check RomM for new ROMs (default: manual; can set to every N minutes when on Wi-Fi). +- **Pull** cadence: how often to check RomM for new ROMs (default: manual, and can be set to every N minutes when on Wi-Fi). - **Push** cadence: how often to upload saves (default: on session end). -- **Full sync**: manual; triggers a full bidirectional sync right now. +- **Full sync**: manual. Triggers a full bidirectional sync right now. ### What pushes back to RomM @@ -96,7 +96,7 @@ If you want Grout to pull from RomM over SSH rather than HTTPS (e.g. on a truste ## Troubleshooting - **Can't see the handheld's Wi-Fi on RomM's network.** Make sure both are on the same SSID / VLAN. -- **Token invalid.** Re-pair; token was revoked or expired. +- **Token invalid.** Re-pair, because the token was revoked or expired. - **Saves aren't syncing.** Check the sync cadence is set to something other than "never", and that the handheld actually has network during the sync window. - **"Device not registered".** The pairing step wasn't completed. Re-run pairing from scratch. diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md index ca6edca2..780f5452 100644 --- a/docs/ecosystem/igir.md +++ b/docs/ecosystem/igir.md @@ -5,7 +5,7 @@ description: Clean up and normalise your ROM collection with Igir before importi # Igir Collection Manager -[Igir](https://igir.io/) is a zero-setup ROM collection manager: sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se; more a pre-processing tool. Useful for cleaning up a library *before* importing into RomM, so RomM's scans have a better-named, better-organised starting point. +[Igir](https://igir.io/) is a zero-setup ROM collection manager: sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se, but more a pre-processing tool. Useful for cleaning up a library *before* importing into RomM, so RomM's scans have a better-named, better-organised starting point. **This is not an official RomM app.** Igir is a separate community project. We document integration here because it's a common workflow and produces a RomM-compatible layout directly. diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md index 2bbfb6e2..364a2113 100644 --- a/docs/ecosystem/index.md +++ b/docs/ecosystem/index.md @@ -13,7 +13,7 @@ Maintained by the RomM team. - **[Argosy Launcher](argosy.md)**: Android launcher that browses and launches your RomM library on mobile. - **[Grout](grout.md)**: Linux handheld companion for muOS / NextUI devices. -- **[Playnite Plugin](playnite-plugin.md)**: Windows desktop; imports your RomM library into [Playnite](https://playnite.link). +- **[Playnite Plugin](playnite-plugin.md)**: Windows desktop, imports your RomM library into [Playnite](https://playnite.link). - **[muOS App](muos-app.md)**: official app for muOS / EmulationStation handhelds to fetch games wirelessly. ## Feeds (for third-party apps) @@ -68,4 +68,4 @@ Not a RomM companion, but useful alongside: Built something RomM-adjacent? Open a PR on [rommapp/docs](https://github.com/rommapp/docs) adding it to [Community Apps](community-apps.md), or drop a link in the [Discord](https://discord.gg/romm) `#community-projects` channel. -We list active, maintained projects. No gate on code quality; we do flag abandoned projects so users know what's current. +We list active, maintained projects. No gate on code quality, but we do flag abandoned projects so users know what's current. diff --git a/docs/ecosystem/kekatsu.md b/docs/ecosystem/kekatsu.md index e2753656..0ea327b5 100644 --- a/docs/ecosystem/kekatsu.md +++ b/docs/ecosystem/kekatsu.md @@ -29,7 +29,7 @@ http://192.168.1.100:3000/api/feeds/kekatsu/nds ## Configuring Kekatsu -Exact config steps depend on your Kekatsu build; the shared concept is "point the app at this URL and it fetches the manifest". Consult Kekatsu's own docs for the current config-file location. +Exact config steps depend on your Kekatsu build, but the shared concept is "point the app at this URL and it fetches the manifest". Consult Kekatsu's own docs for the current config-file location. ## File format @@ -43,11 +43,11 @@ Kekatsu can send basic auth. Either configure it on the DS side or enable `DISAB The DS's original Wi-Fi hardware supports WEP and an older WPA variant only. Modern home routers usually don't. Workarounds: -- **Dedicated DS-friendly SSID.** Many routers allow per-SSID security; add a WEP one just for the DS. +- **Dedicated DS-friendly SSID.** Many routers allow per-SSID security, so add a WEP one just for the DS. - **Travel router in bridge mode.** A cheap travel router configured for WEP uplinks to your main (secure) network. - **Use a DSi, 3DS, or homebrew replacement driver.** These support modern security. -If none of this is appealing, Kekatsu-over-LAN isn't going to work; fall back to sideloading via flashcart or similar. +If none of this is appealing, Kekatsu-over-LAN isn't going to work. Fall back to sideloading via flashcart or similar. ## Troubleshooting diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md index 891c397d..3f891cdf 100644 --- a/docs/ecosystem/muos-app.md +++ b/docs/ecosystem/muos-app.md @@ -21,8 +21,8 @@ description: Official RomM app for muOS and EmulationStation handhelds, fetch ga This page covers the **muOS App**: a lightweight client focused on game fetching. For the fuller push/pull sync experience (saves back to RomM, play-session reporting), use [Grout](grout.md) instead. They're two different clients for the same family of devices. -- **muOS App**: lightweight; pulls ROMs, no save sync. -- **Grout**: full sync; ROMs + saves + states + play sessions. +- **muOS App**: lightweight, pulls ROMs, no save sync. +- **Grout**: full sync, ROMs + saves + states + play sessions. Pick based on what you need. @@ -34,7 +34,7 @@ Installation uses muOS's [Archive Manager](https://muos.dev/installation/archive 2. Move the `.muxapp` file to `/mnt/mmc/ARCHIVE/` on the device (USB, SD swap, or SSH). 3. On the device: **Applications → Archive Manager** → select `RomM.muOS.x.x.x.muxapp` → install. 4. Once installed, copy `/mnt/mmc/MUOS/application/RomM/env.template` to `.env` in the same folder. -5. Edit `.env` (SSH works well; so does any method that writes to SD card): +5. Edit `.env` (SSH works well, as does any method that writes to SD card): ```dotenv HOST=http://192.168.1.100:3000 @@ -65,12 +65,12 @@ The handheld has to reach your RomM instance over Wi-Fi. Simplest setup: - **Same LAN.** Handheld and RomM server on the same SSID. `HOST` = server IP + port. -- **Plain HTTP works** on a trusted LAN; no reverse proxy needed. +- **Plain HTTP works** on a trusted LAN, and no reverse proxy is needed. More-involved setups: - **Reverse proxy with TLS.** `HOST=https://romm.example.com`. HTTPS works but introduces cert-validation risk on handhelds (some fail strict TLS). -- **Remote access via VPN.** Install Tailscale or similar on the handheld (if supported); lets the handheld reach RomM from outside the LAN. +- **Remote access via VPN.** Install Tailscale or similar on the handheld (if supported). This lets the handheld reach RomM from outside the LAN. ## Using the app @@ -90,7 +90,7 @@ If you need these, Grout is the app. - **Can't connect.** Wrong `HOST` in `.env`, or the handheld isn't on the same network as RomM. Ping RomM's IP from the handheld's shell to confirm reachability. - **"Authentication failed".** Password wrong, or `DISABLE_USERPASS_LOGIN=true` on the RomM side. Either re-enable user/pass login or use a token once supported. -- **Downloaded games don't show in the platform.** Refresh the library from muOS's UI. If they still don't appear, the platform folder in `HOST_PATH` is wrong; check muOS's expected layout. +- **Downloaded games don't show in the platform.** Refresh the library from muOS's UI. If they still don't appear, the platform folder in `HOST_PATH` is wrong. Check muOS's expected layout. ## See also diff --git a/docs/ecosystem/pkgj.md b/docs/ecosystem/pkgj.md index 647c7641..7eea6cda 100644 --- a/docs/ecosystem/pkgj.md +++ b/docs/ecosystem/pkgj.md @@ -5,14 +5,14 @@ description: Install PS Vita and PSP games from your RomM library via pkgj homeb # pkgj -[pkgj](https://github.com/blastrock/pkgj) is PS Vita homebrew for installing `.pkg`-format games and DLC. Default config points at well-known community URLs; point it at RomM's feed endpoints instead and you can install from your library over Wi-Fi. +[pkgj](https://github.com/blastrock/pkgj) is PS Vita homebrew for installing `.pkg`-format games and DLC. Default config points at well-known community URLs, but you can point it at RomM's feed endpoints instead and install from your library over Wi-Fi. ## Prerequisites - **PS Vita** with [pkgj](https://github.com/blastrock/pkgj) installed. - A way to edit files on the Vita: [VitaShell](https://github.com/TheOfficialFloW/VitaShell) works well. -- **RomM reachable from the Vita**: same LAN ideal; HTTP or HTTPS both work. -- Your games stored as `.pkg` files (pkgj requires this format; it won't work with `.iso` or other formats). +- **RomM reachable from the Vita**: same LAN ideal, and HTTP or HTTPS both work. +- Your games stored as `.pkg` files (pkgj requires this format, and it won't work with `.iso` or other formats). ## Feed URLs @@ -59,7 +59,7 @@ If you have non-`.pkg` files you want on the Vita, you'll need to convert them o The pkgi feeds honour basic auth. If your RomM doesn't have `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`, pkgj sends basic auth headers. -Unlike [Tinfoil](tinfoil.md), pkgj handles auth natively: you don't have to turn off download auth on RomM to use it. Still, some users prefer disabling auth for a smoother first-time flow; either path works. +Unlike [Tinfoil](tinfoil.md), pkgj handles auth natively: you don't have to turn off download auth on RomM to use it. Still, some users prefer disabling auth for a smoother first-time flow. Either path works. ## Troubleshooting diff --git a/docs/ecosystem/tinfoil.md b/docs/ecosystem/tinfoil.md index bcdf446e..7a836190 100644 --- a/docs/ecosystem/tinfoil.md +++ b/docs/ecosystem/tinfoil.md @@ -17,8 +17,8 @@ description: Install Nintendo Switch games from your RomM library over Wi-Fi via - **RomM 3.5.0 or newer.** Tinfoil feeds landed in that release. Much better in 5.0. - **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`** on your RomM instance. Tinfoil can't send a bearer token, so the downloads endpoint has to be openable. **Only enable this when RomM isn't directly exposed to the public internet.** See [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass). -- **Tinfoil installed on the Switch.** Setup varies; follow Tinfoil's own docs. -- **A Switch that can reach RomM over Wi-Fi.** Same LAN is easiest; remote reachability requires reverse proxy + cert that the Switch accepts. +- **Tinfoil installed on the Switch.** Setup varies, so follow Tinfoil's own docs. +- **A Switch that can reach RomM over Wi-Fi.** Same LAN is easiest. Remote reachability requires reverse proxy + cert that the Switch accepts. ## Feed URL @@ -37,7 +37,7 @@ No authentication: the endpoint works as long as `DISABLE_DOWNLOAD_ENDPOINT_AUTH - **Host:** RomM's hostname or IP. - **Port:** RomM's port (usually 80 or 443). - **Path:** `/api/feeds/tinfoil` - - **Username:** your RomM username (optional; Tinfoil can send basic auth; RomM tries it). + - **Username:** your RomM username (optional, Tinfoil can send basic auth, and RomM tries it). - **Password:** your RomM password. - **Title:** anything (e.g. `RomM Switch`). - **Enabled:** yes. @@ -87,7 +87,7 @@ If you put RomM behind a reverse proxy, the proxy can handle auth separately fro - Proxy challenges for basic auth before reaching RomM. - RomM itself has `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`. -Tinfoil sends basic auth upstream; proxy accepts and forwards; RomM serves. +Tinfoil sends basic auth upstream, proxy accepts and forwards, and RomM serves. This gets you authenticated Tinfoil feeds without making RomM itself world-readable. @@ -95,7 +95,7 @@ This gets you authenticated Tinfoil feeds without making RomM itself world-reada - **"can't get list: list is empty".** Either your RomM library has no `.nsp`/`.xci` files that Tinfoil recognises, or filenames lack title IDs. - **Tinfoil connects but nothing in New Games.** Title IDs missing from filenames. Rename. -- **Tinfoil can't connect at all.** LAN reachability issue, or wrong port in the feed setup. Try `http://:/api/feeds/tinfoil` in a browser; you should get JSON. +- **Tinfoil can't connect at all.** LAN reachability issue, or wrong port in the feed setup. Try `http://:/api/feeds/tinfoil` in a browser. You should get JSON. - **Downloads fail with 401.** `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` isn't set on RomM, or you forgot to restart the container after setting it. ## See also diff --git a/docs/ecosystem/webrcade.md b/docs/ecosystem/webrcade.md index 43961f78..293881c8 100644 --- a/docs/ecosystem/webrcade.md +++ b/docs/ecosystem/webrcade.md @@ -32,7 +32,7 @@ The `/api/feeds/webrcade` endpoint sends basic auth if WebRcade provides credent - Configure basic auth on WebRcade's feed-add screen (if it offers that), OR - Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` on RomM. -The same security caveats apply; see [Tinfoil prerequisites](tinfoil.md#prerequisites) for context on turning off download auth. +The same security caveats apply. See [Tinfoil prerequisites](tinfoil.md#prerequisites) for context on turning off download auth. ## RomM vs WebRcade @@ -44,9 +44,9 @@ Why would you use WebRcade over RomM's built-in player? When to stay with RomM's player: -- **You want library management.** WebRcade is frontend-only; RomM owns the metadata and scanning. +- **You want library management.** WebRcade is frontend-only, but RomM owns the metadata and scanning. - **You want user accounts + collections + per-user progress.** WebRcade is single-user-ish. -- **You want [Netplay](../using/netplay.md).** RomM has it; WebRcade doesn't. +- **You want [Netplay](../using/netplay.md).** RomM has it, but WebRcade doesn't. Totally reasonable to run both: WebRcade as a launcher UI pointed at RomM for the library. diff --git a/docs/getting-started/concepts.md b/docs/getting-started/concepts.md index 387724d6..97152e52 100644 --- a/docs/getting-started/concepts.md +++ b/docs/getting-started/concepts.md @@ -32,8 +32,8 @@ The process of walking the library, hashing files, calling metadata providers, a A named grouping of ROMs. Three flavours: - **Standard**: a hand-curated list. You pick what's in it. -- **Smart**: rule-based and auto-populating. Define a query ("all SNES games rated 4+ stars"); RomM keeps it in sync. -- **Virtual**: auto-generated by RomM itself (by genre, developer, year, tag). Not user-editable; toggle on/off in UI settings. +- **Smart**: rule-based and auto-populating. Define a query ("all SNES games rated 4+ stars") and RomM keeps it in sync. +- **Virtual**: auto-generated by RomM itself (by genre, developer, year, tag). Not user-editable, but you can toggle on/off in UI settings. ## Asset @@ -41,7 +41,7 @@ User-uploaded content attached to a ROM: save files, emulator states, screenshot ## Firmware -BIOS or system firmware required for certain emulators (PS1, GBA, Saturn, etc.). Lives under `/romm/library/bios/{platform}/` or `/romm/library/{platform}/bios/` (depending on which [folder structure](folder-structure.md) you chose). Uploaded via the UI; managed by admins/editors. +BIOS or system firmware required for certain emulators (PS1, GBA, Saturn, etc.). Lives under `/romm/library/bios/{platform}/` or `/romm/library/{platform}/bios/` (depending on which [folder structure](folder-structure.md) you chose). Uploaded via the UI and managed by admins/editors. ## User @@ -50,21 +50,21 @@ An account with a role (Viewer, Editor, Admin) and a set of scopes. Can be creat ## Role & Scope - **Roles** (Viewer / Editor / Admin) are convenient bundles of scopes. -- **Scopes** are fine-grained permissions (`roms.read`, `collections.write`, `users.write`, `tasks.run`; 19 total). +- **Scopes** are fine-grained permissions (`roms.read`, `collections.write`, `users.write`, `tasks.run`, 19 total). Tokens and OIDC sessions carry subsets of scopes. Every endpoint requires specific scopes. See the [scope matrix](../administration/users-and-roles.md#scope-matrix). ## Client API Token -A long-lived bearer token scoped to a user. Used by companion apps (Argosy, Grout, Playnite, custom scripts) to authenticate. Each user gets up to 25 active tokens; tokens can be paired to devices via a short code. See [Client API Tokens](../ecosystem/client-api-tokens.md). +A long-lived bearer token scoped to a user. Used by companion apps (Argosy, Grout, Playnite, custom scripts) to authenticate. Each user gets up to 25 active tokens, and tokens can be paired to devices via a short code. See [Client API Tokens](../ecosystem/client-api-tokens.md). ## Device -A registered endpoint that syncs with RomM: a handheld running Grout, an Android phone running Argosy, a SteamDeck running DeckRommSync. Devices pull saves and states; some push them back after a session. See [Device Sync Protocol](../ecosystem/device-sync-protocol.md). +A registered endpoint that syncs with RomM: a handheld running Grout, an Android phone running Argosy, a SteamDeck running DeckRommSync. Devices pull saves and states, and some push them back after a session. See [Device Sync Protocol](../ecosystem/device-sync-protocol.md). ## Play Session -A timestamped record of someone playing a ROM: start, end, duration, device. Used by RomM's stats, the Continue Playing ribbon, and per-ROM playtime totals. Ingested automatically when playing in-browser; companion apps push them via API. +A timestamped record of someone playing a ROM: start, end, duration, device. Used by RomM's stats, the Continue Playing ribbon, and per-ROM playtime totals. Ingested automatically when playing in-browser, and companion apps push them via API. ## Task @@ -73,7 +73,7 @@ A unit of background work: scan, metadata sync, cleanup, device sync. Runs throu ## Resources & Assets directories (confusing, named similarly, not the same thing) - **`/romm/resources`**: **machine-managed**. Cover art, screenshots, manuals fetched from metadata providers. Can be rebuilt from a rescan. -- **`/romm/assets`**: **user-owned**. Saves, states, user-uploaded screenshots. Back this up; it's not recoverable. +- **`/romm/assets`**: **user-owned**. Saves, states, user-uploaded screenshots. Back this up because it's not recoverable. ## Console Mode @@ -81,11 +81,11 @@ A separate `/console` UI optimised for gamepads and TV displays: spatial navigat ## Kiosk Mode -A server-side setting (`KIOSK_MODE=true`) that turns every read endpoint into unauthenticated access. Anonymous visitors can browse; nobody can write. Useful for public demos and wall displays. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). +A server-side setting (`KIOSK_MODE=true`) that turns every read endpoint into unauthenticated access. Anonymous visitors can browse, but nobody can write. Useful for public demos and wall displays. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). ## EmulatorJS / Ruffle -The two in-browser emulators. **EmulatorJS** handles retro consoles (NES, SNES, N64, PSX, Saturn, and 20+ more cores). **Ruffle** handles Flash / Shockwave browser games. Both launch from the ROM detail page; both can be disabled per-instance if you don't want them. See [In-Browser Play](../using/in-browser-play.md). +The two in-browser emulators. **EmulatorJS** handles retro consoles (NES, SNES, N64, PSX, Saturn, and 20+ more cores). **Ruffle** handles Flash / Shockwave browser games. Both launch from the ROM detail page, and both can be disabled per-instance if you don't want them. See [In-Browser Play](../using/in-browser-play.md). ## Netplay diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md index 276f43c4..3b14d9b5 100644 --- a/docs/getting-started/first-scan.md +++ b/docs/getting-started/first-scan.md @@ -29,7 +29,7 @@ The page switches to live mode: - Per-platform **accordion panels** show counts update live: total found, matched, unmatched. - You can click a **matched ROM** while the scan is still running to see what metadata RomM pulled, with no need to wait for the full run. -First scans on big libraries take a while. Expect ~1 second per ROM with a fast network to IGDB/ScreenScraper; hashing (which runs unless you disabled it) adds IO time proportional to file size. +First scans on big libraries take a while. Expect ~1 second per ROM with a fast network to IGDB/ScreenScraper, and hashing (which runs unless you disabled it) adds IO time proportional to file size. ## What "matched" means @@ -44,10 +44,10 @@ An **unmatched** ROM means no provider recognised it. Common causes: - Filename is too generic (`game.gba`). - Bad rip, intro / patch applied, or a regional variant no provider has indexed. -- Platform folder misnamed: the scanner queries providers scoped to the detected platform; wrong platform = no results. +- Platform folder misnamed: the scanner queries providers scoped to the detected platform, so wrong platform = no results. - Metadata provider credentials wrong or rate-limited: check the scan log for errors. -Most of these are fixable; see [Scanning Troubleshooting](../troubleshooting/scanning.md). +Most of these are fixable. See [Scanning Troubleshooting](../troubleshooting/scanning.md). ## When the scan finishes @@ -60,7 +60,7 @@ Click the **RomM logo** (top-left) to go home. You should see: From here, typical next steps: - **Browse**: click a platform card, flip through the grid. -- **Fix unmatched ROMs**: rename or re-tag; re-run an **Unmatched** scan to pick them up. See [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). +- **Fix unmatched ROMs**: rename or re-tag, then re-run an **Unmatched** scan to pick them up. See [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). - **Tweak priorities**: if ScreenScraper's covers are nicer than IGDB's for your library, reorder `scan.priority.artwork` in [`config.yml`](../reference/configuration-file.md). - **Add more users**: [Invitations & Registration](../administration/invitations-and-registration.md). diff --git a/docs/getting-started/folder-structure.md b/docs/getting-started/folder-structure.md index 98415e7b..b70115ea 100644 --- a/docs/getting-started/folder-structure.md +++ b/docs/getting-started/folder-structure.md @@ -7,7 +7,7 @@ description: How to organise your ROM library on disk so RomM can scan and match # Folder Structure -RomM expects your library to be organised in one of two layouts. It tries **Structure A** first, and falls back to **Structure B** if A isn't found. This auto-detection is per-library, so you don't pick one up front; just arrange files the way you prefer, and RomM figures it out. +RomM expects your library to be organised in one of two layouts. It tries **Structure A** first, and falls back to **Structure B** if A isn't found. This auto-detection is per-library, so you don't pick one up front. Just arrange files the way you prefer, and RomM figures it out. ## The two layouts @@ -164,7 +164,7 @@ Some games come as **folders** instead of single files: multi-disc, DLC, manuals ## Customising behaviour -The on-disk layout is only half the story. Per-library exclusions, custom platform bindings, and metadata source priority all live in [`config.yml`](../reference/configuration-file.md). You can edit the file directly, or go through **Administration → Library Management** in the web UI; they're two views of the same data. +The on-disk layout is only half the story. Per-library exclusions, custom platform bindings, and metadata source priority all live in [`config.yml`](../reference/configuration-file.md). You can edit the file directly, or go through **Administration → Library Management** in the web UI. They're two views of the same data. ## Naming convention diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index 5dbb7aff..792eab01 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -13,14 +13,14 @@ You'll need: - [Docker](https://docs.docker.com/get-docker/) and Docker Compose installed on the host. - Your ROM files organised in the expected [folder structure](folder-structure.md). -- API credentials for at least one [metadata provider](../administration/metadata-providers.md). IGDB + ScreenScraper is the recommended pairing; RomM will run without any provider configured, but matching quality will suffer. +- API credentials for at least one [metadata provider](../administration/metadata-providers.md). IGDB + ScreenScraper is the recommended pairing. RomM will run without any provider configured, but matching quality will suffer. !!! warning "Metadata providers are recommended" RomM works without a metadata API for basic use, but setup problems and companion-app integrations (e.g. Playnite) can fail without them. Setting up **IGDB** API keys before your first scan is strongly recommended. ## 1. Write your `docker-compose.yml` -Start from the reference file shipped in the RomM repo; a known-good, minimally-edited version is included below. Save it as `docker-compose.yml` in an empty directory on your host. +Start from the reference file shipped in the RomM repo. A known-good, minimally-edited version is included below. Save it as `docker-compose.yml` in an empty directory on your host. ???+ example "docker-compose.yml" ``` yaml @@ -31,7 +31,7 @@ You'll want to edit the following values before launching: | Where | Variable | What to put | | --- | --- | --- | -| `romm-db` service | `MARIADB_ROOT_PASSWORD` | A long random password. Generate one; don't reuse. | +| `romm-db` service | `MARIADB_ROOT_PASSWORD` | A long random password. Generate one. Don't reuse. | | `romm-db` service | `MARIADB_PASSWORD` | A separate long random password for the `romm-user`. | | `romm` service | `DB_PASSWD` | Must match `MARIADB_PASSWORD` above. | | `romm` service | `ROMM_AUTH_SECRET_KEY` | Generate with `openssl rand -hex 32`. Keep it secret. | diff --git a/docs/getting-started/what-is-new-in-5.md b/docs/getting-started/what-is-new-in-5.md index a4b66cba..78c51776 100644 --- a/docs/getting-started/what-is-new-in-5.md +++ b/docs/getting-started/what-is-new-in-5.md @@ -43,7 +43,7 @@ English (US/GB), Spanish, French, German, Italian, Portuguese (BR), Japanese, Ko ### Time to Beat & RetroAchievements tabs -HowLongToBeat completion times and RetroAchievements progression now surface as dedicated tabs on every game's detail page. Both need provider credentials; see [Metadata Providers](../administration/metadata-providers.md). +HowLongToBeat completion times and RetroAchievements progression now surface as dedicated tabs on every game's detail page. Both need provider credentials. See [Metadata Providers](../administration/metadata-providers.md). ### Bulk downloads, QR codes, copy-link @@ -126,7 +126,7 @@ Two endpoints: `/ws/socket.io` for general live updates (scan progress, notifica | **Database schema** | Alembic migrates automatically, but back up first. | | **Env var names** | A few renames around scheduled-task cron vars and OIDC role mapping. See [migration table](../releases/upgrading-to-5.0.md). | | **`config.yml`** | New sections for `scan.region`, `scan.language`, `scan.media`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings carry over. | -| **URL structure** | Docs URLs restructured (you're reading the new ones). Every old URL redirects; see [the redirect map](../releases/upgrading-to-5.0.md) in the migration guide. | +| **URL structure** | Docs URLs restructured (you're reading the new ones). Every old URL redirects. See [the redirect map](../releases/upgrading-to-5.0.md) in the migration guide. | | **Image tags** | `:slim` and `:5.0.0-slim` are new options alongside `:latest` and `:5.0.0`. | ## Where to go from here diff --git a/docs/index.md b/docs/index.md index 0eff3a11..3e8e52c7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,7 +17,7 @@ Welcome to the **RomM Project**, the premier self-hosted, open source ROM manage Website · Demo · Discord
-RomM (ROM Manager) lets you scan, enrich, organise, and play your game collection from a clean web UI, with deep metadata from IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, LaunchBox, and more; in-browser play via EmulatorJS and Ruffle; companion apps for Android, handhelds, and desktop; and a first-class multi-user experience with OIDC SSO. +RomM (ROM Manager) lets you scan, enrich, organise, and play your game collection from a clean web UI, with metadata from IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, LaunchBox, and more; in-browser play via EmulatorJS and Ruffle; companion apps for Android, handhelds, and desktop; and a first-class multi-user experience with OIDC SSO. ## Where do you want to go? @@ -75,7 +75,7 @@ RomM (ROM Manager) lets you scan, enrich, organise, and play your game collectio ## Philosophy -RomM is built for its users, not for shareholders. Self-hosted, open-source, no tracking, no upsells. The core app is licensed under [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/); other projects in the umbrella use permissive licenses ([GPLv3](https://choosealicense.com/licenses/gpl-3.0/) for software, [CC0](https://choosealicense.com/licenses/cc0-1.0/) for documentation). +RomM is built for its users, not for shareholders. Self-hosted, open-source, no tracking, no upsells. The core app is licensed under [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/). Other projects in the umbrella use permissive licenses ([GPLv3](https://choosealicense.com/licenses/gpl-3.0/) for software, [CC0](https://choosealicense.com/licenses/cc0-1.0/) for documentation). **RomM is and will always be free and open-source software.** diff --git a/docs/install/backup-and-restore.md b/docs/install/backup-and-restore.md index 6dc667d6..01b545fb 100644 --- a/docs/install/backup-and-restore.md +++ b/docs/install/backup-and-restore.md @@ -14,9 +14,9 @@ This page covers both routine backups and migrating RomM to a new host: same pro | **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical**: back this up nightly. | | **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical**: back this up nightly. | | **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes, but small and painful to recreate. | -| `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority; can be re-downloaded on a rescan. Including it speeds up recovery. | -| `/redis-data` | Task queue state | Low priority; in-flight tasks only; lost tasks can be re-run. | -| **`/romm/library`** | Your ROM files | Back this up **separately**; it's your source data and you should already have a backup strategy for it independent of RomM. | +| `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority, and can be re-downloaded on a rescan. Including it speeds up recovery. | +| `/redis-data` | Task queue state | Low priority, in-flight tasks only, and lost tasks can be re-run. | +| **`/romm/library`** | Your ROM files | Back this up **separately**. It's your source data and you should already have a backup strategy for it independent of RomM. | ## Routine backup @@ -69,7 +69,7 @@ rsync -a --delete /srv/romm/config/ "$DEST/config/" find "$DEST" -maxdepth 1 -name 'db-*.sql.gz' -mtime +14 -delete ``` -Offsite it however you already do (rclone to B2/S3, restic, borg, Proxmox Backup Server); RomM doesn't care. +Offsite it however you already do (rclone to B2/S3, restic, borg, Proxmox Backup Server). RomM doesn't care. ## Restore @@ -165,4 +165,4 @@ Before upgrading to a new RomM major version: 4. Start back up: `docker compose start`. 5. Pull the new image and upgrade. -If the upgrade blows up, restore the dump + snapshot. For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md) first; there are breaking changes. +If the upgrade blows up, restore the dump + snapshot. For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md) first, because there are breaking changes. diff --git a/docs/install/databases.md b/docs/install/databases.md index b7e53f10..f06c884c 100644 --- a/docs/install/databases.md +++ b/docs/install/databases.md @@ -5,7 +5,7 @@ description: Supported database drivers for RomM, connection strings, and recomm # Databases -RomM uses SQLAlchemy + Alembic for persistence. Four drivers are supported; pick based on what you already run. +RomM uses SQLAlchemy + Alembic for persistence. Four drivers are supported, so pick based on what you already run. | Driver | `ROMM_DB_DRIVER` | Image | Default port | Notes | | --- | --- | --- | --- | --- | @@ -14,7 +14,7 @@ RomM uses SQLAlchemy + Alembic for persistence. Four drivers are supported; pick | **PostgreSQL** | `postgresql` | `postgres:16` | `5432` | Supported. Use if you already run Postgres. | | **SQLite** | `sqlite` | _(file on disk)_ | n/a | Dev/tiny deployments only. Not recommended for anything multi-user or larger than a few hundred games. | -RomM runs Alembic migrations automatically on startup; no manual step when upgrading. +RomM runs Alembic migrations automatically on startup. No manual step when upgrading. ## MariaDB (default) @@ -109,7 +109,7 @@ services: ## SQLite (not recommended) -Set `ROMM_DB_DRIVER=sqlite`. The DB file lives at `{ROMM_BASE_PATH}/database`. No separate container required; useful for a laptop demo or a one-user install on a low-power box. Don't use this for anything you care about: +Set `ROMM_DB_DRIVER=sqlite`. The DB file lives at `{ROMM_BASE_PATH}/database`. No separate container required, and useful for a laptop demo or a one-user install on a low-power box. Don't use this for anything you care about: - No concurrent writers → scans and API calls block each other. - The file can corrupt if the container is killed mid-write. @@ -124,13 +124,13 @@ environment: - DB_QUERY_JSON={"ssl": "true", "connect_timeout": "5"} ``` -Exact keys depend on the driver; see SQLAlchemy / the driver's docs. +Exact keys depend on the driver. See SQLAlchemy / the driver's docs. ## Which should I pick? - **Sticking with defaults?** MariaDB. That's what the reference compose uses and what the team tests against. - **Already run Postgres?** Postgres. No reason to add a second DB engine. -- **Single-user laptop demo?** SQLite is fine; upgrade before adding anyone else. +- **Single-user laptop demo?** SQLite is fine, but upgrade before adding anyone else. - **External managed DB?** Any of MariaDB / MySQL / Postgres. Point `DB_HOST` at it and configure TLS via `DB_QUERY_JSON`. -Don't switch DB drivers on a running install without a plan; migrating the data requires a dump + reload, covered in [Backup & Restore](backup-and-restore.md). +Don't switch DB drivers on a running install without a plan. Migrating the data requires a dump + reload, covered in [Backup & Restore](backup-and-restore.md). diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index dfd31fce..2f574637 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -5,13 +5,13 @@ description: Canonical Docker Compose reference for a production RomM 5.0 deploy # Docker Compose -The canonical way to run RomM is with Docker Compose. This page describes the full reference stack; the shorter happy-path walkthrough lives in [Quick Start](../getting-started/quick-start.md). +The canonical way to run RomM is with Docker Compose. This page describes the full reference stack. The shorter happy-path walkthrough lives in [Quick Start](../getting-started/quick-start.md). The RomM stack has three parts: 1. **`romm`**: the application container (FastAPI backend, Vue frontend, nginx, and an embedded Redis/Valkey worker). -2. **A database**: MariaDB by default; MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. -3. **Redis or Valkey**: required for background tasks, session storage, and queue coordination. In the default `full-image` this runs inside the RomM container; for production you'll typically split it out. See [Redis or Valkey](redis-or-valkey.md). +2. **A database**: MariaDB by default, but MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. +3. **Redis or Valkey**: required for background tasks, session storage, and queue coordination. In the default `full-image` this runs inside the RomM container, but for production you'll typically split it out. See [Redis or Valkey](redis-or-valkey.md). ## Reference `docker-compose.yml` @@ -26,7 +26,7 @@ The RomM stack has three parts: | Field | Value | Notes | | --- | --- | --- | | `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`. Pin to a specific tag (`5.0.0`) for production. See [Image Variants](image-variants.md) for `slim` vs `full`. | -| `ports` | `80:8080` | Container listens on `8080`. Expose through a reverse proxy in production; see [Reverse Proxy](reverse-proxy.md). | +| `ports` | `80:8080` | Container listens on `8080`. Expose through a reverse proxy in production. See [Reverse Proxy](reverse-proxy.md). | | `volumes` | see below | RomM writes to four distinct paths inside the container. | | `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | @@ -36,14 +36,14 @@ The RomM stack has three parts: | --- | --- | --- | | `/romm/library` | Your ROM files. Typically mounted **read-only**. | No. This is your source data, back it up separately. | | `/romm/assets` | User uploads: saves, states, uploaded screenshots. | **Critical.** Back this up with your DB. | -| `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low; can be re-downloaded on a rescan. | +| `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low, and can be re-downloaded on a rescan. | | `/romm/config` | Holds `config.yml`. | **Critical.** Hand-tuned config, back it up. | See [Backup & Restore](backup-and-restore.md) for the full procedure. #### Core environment variables -Minimal set; see the [Environment Variables reference](../reference/environment-variables.md) for the complete list. +Minimal set. See the [Environment Variables reference](../reference/environment-variables.md) for the complete list. | Variable | What it does | | --- | --- | @@ -58,7 +58,7 @@ Minimal set; see the [Environment Variables reference](../reference/environment- | Field | Value | Notes | | --- | --- | --- | | `image` | `mariadb:latest` | Pin to a major version (`mariadb:11`) for production. | -| `volumes` | `mysql_data:/var/lib/mysql` | Back this up; it's your entire catalogue. | +| `volumes` | `mysql_data:/var/lib/mysql` | Back this up. It's your entire catalogue. | | `healthcheck` | `healthcheck.sh --connect --innodb_initialized` | RomM waits for this. | The default compose file uses MariaDB because it requires no extra driver configuration. To use PostgreSQL, swap the image for `postgres:16`, set `ROMM_DB_DRIVER=postgresql`, and point `DB_PORT` at `5432`. See [Databases](databases.md) for the full swap. @@ -69,7 +69,7 @@ The quick-start compose file is functional but not production-ready. Before expo - **Pin image tags.** `rommapp/romm:latest` moves, so use `rommapp/romm:5.0.0` (or whatever release you're on). - **Use a reverse proxy with HTTPS.** The built-in nginx listens on `8080` and terminates plain HTTP. Put Traefik, Caddy, or nginx in front with TLS. See [Reverse Proxy](reverse-proxy.md). -- **Split out Redis/Valkey.** The `full-image` embeds Redis; for production run a dedicated container and set `REDIS_HOST`. See [Redis or Valkey](redis-or-valkey.md). +- **Split out Redis/Valkey.** The `full-image` embeds Redis, so for production run a dedicated container and set `REDIS_HOST`. See [Redis or Valkey](redis-or-valkey.md). - **Set a non-default `MARIADB_ROOT_PASSWORD`.** And don't reuse it for `MARIADB_PASSWORD`. - **Mount the library read-only** unless you need RomM to write into it: `- /path/to/library:/romm/library:ro`. - **Use Docker secrets for credentials** if your orchestrator supports them. RomM recognises `ROMM_AUTH_SECRET_KEY_FILE` for loading the secret from a file mount. @@ -82,4 +82,4 @@ docker compose pull docker compose up -d ``` -Alembic migrations run automatically on startup. Read the [release notes](../releases/index.md) before every major-version bump; the 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). +Alembic migrations run automatically on startup. Read the [release notes](../releases/index.md) before every major-version bump. The 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). diff --git a/docs/install/image-variants.md b/docs/install/image-variants.md index 6c88d51c..0f77dd3a 100644 --- a/docs/install/image-variants.md +++ b/docs/install/image-variants.md @@ -5,7 +5,7 @@ description: Choose between the slim and full RomM container images. # Image Variants -RomM publishes two production image variants. They're interchangeable at the config level; pick based on whether you want in-browser emulation. +RomM publishes two production image variants. They're interchangeable at the config level, so pick based on whether you want in-browser emulation. | Variant | Tag | Includes | Approx size | When to pick | | --- | --- | --- | --- | --- | @@ -28,11 +28,11 @@ services: docker compose up -d ``` -No data migration is required; saves, states, metadata, and `config.yml` are the same across variants. +No data migration is required. Saves, states, metadata, and `config.yml` are the same across variants. ## Dev images -`rommapp/romm:dev-slim` and `rommapp/romm:dev-full` track the `master` branch with hot-reload-friendly entrypoints. Useful for testing changes against a real library; not recommended for anything you care about. See [Development Setup](../developers/development-setup.md). +`rommapp/romm:dev-slim` and `rommapp/romm:dev-full` track the `master` branch with hot-reload-friendly entrypoints. Useful for testing changes against a real library, but not recommended for anything you care about. See [Development Setup](../developers/development-setup.md). ## What's actually different @@ -41,4 +41,4 @@ The two images share the same backend code, frontend bundle, nginx config, and e - **EmulatorJS** (~700 MB uncompressed): the in-browser retro emulator bundle. - **Ruffle** (~20 MB): the Flash/Shockwave emulator. -If you set `ENABLE_EMULATORJS=false` and `ENABLE_RUFFLE=false` on the full image, behaviour matches slim; only the image is larger. Setting them to `true` on the slim image will cause players to 404 at runtime. +If you set `ENABLE_EMULATORJS=false` and `ENABLE_RUFFLE=false` on the full image, behaviour matches slim, and only the image is larger. Setting them to `true` on the slim image will cause players to 404 at runtime. diff --git a/docs/install/index.md b/docs/install/index.md index d577586d..5a6a1357 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -5,7 +5,7 @@ description: Pick a deployment path: Docker Compose, Unraid, Synology, TrueNAS, # Install & Deploy -RomM is distributed as a Docker image. Every supported deployment runs the same container; the differences are in who manages it. +RomM is distributed as a Docker image. Every supported deployment runs the same container, and the differences are in who manages it. ## Pick your path diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index ef9ecd08..6604525a 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -8,12 +8,12 @@ description: Deploy RomM on Kubernetes with manifest examples, required quirks, There's no official Helm chart for RomM in 5.0. This page walks through a production-grade manifest set and points at the community charts worth looking at. !!! note "Community-maintained charts" - If you'd rather not hand-author manifests, there are community Helm charts around; check [ArtifactHub](https://artifacthub.io/packages/search?ts_query_web=romm) and the `#kubernetes` channel in the [RomM Discord](https://discord.gg/P5HtHnhUDH). The RomM team doesn't publish or formally support any chart today, so pick one with active maintenance and recent releases. + If you'd rather not hand-author manifests, there are community Helm charts around. Check [ArtifactHub](https://artifacthub.io/packages/search?ts_query_web=romm) and the `#kubernetes` channel in the [RomM Discord](https://discord.gg/P5HtHnhUDH). The RomM team doesn't publish or formally support any chart today, so pick one with active maintenance and recent releases. ## What you need - A cluster running Kubernetes 1.27+. -- Persistent storage for the DB, cache, and RomM's assets/resources/config (block or RWX; see below). +- Persistent storage for the DB, cache, and RomM's assets/resources/config (block or RWX, see below). - An Ingress controller (nginx-ingress, Traefik, etc.) for external access. - cert-manager or equivalent for HTTPS. @@ -199,7 +199,7 @@ spec: ## Persistent Volume Claims -One PVC per volume. Sizes are rough; tune to your library. +One PVC per volume. Sizes are rough, so tune to your library. ```yaml apiVersion: v1 @@ -271,7 +271,7 @@ spec: port: { number: 80 } ``` -`proxy-body-size: "0"` is important; the default is 1 MB and will reject every ROM upload with HTTP 413. +`proxy-body-size: "0"` is important, because the default is 1 MB and will reject every ROM upload with HTTP 413. ## Scaling notes @@ -285,7 +285,7 @@ spec: kubectl set image -n romm deployment/romm romm=rommapp/romm:5.0.1 ``` -Alembic runs on startup; migrations happen automatically. Before major-version upgrades, read the release notes and take a backup (see [Backup & Restore](backup-and-restore.md)). +Alembic runs on startup, so migrations happen automatically. Before major-version upgrades, read the release notes and take a backup (see [Backup & Restore](backup-and-restore.md)). ## Troubleshooting diff --git a/docs/install/redis-or-valkey.md b/docs/install/redis-or-valkey.md index f5b7ca36..e3aa6b78 100644 --- a/docs/install/redis-or-valkey.md +++ b/docs/install/redis-or-valkey.md @@ -16,9 +16,9 @@ Both **Redis** and **Valkey** (the open-source Redis fork maintained by the Linu ## Option A: embedded (default) -The default `full-image` container runs a Redis server inside itself. Zero config, fine for single-instance deployments. The `REDIS_HOST` / `REDIS_PORT` defaults (`localhost:6379`) point at the embedded instance; leave them alone. +The default `full-image` container runs a Redis server inside itself. Zero config, fine for single-instance deployments. The `REDIS_HOST` / `REDIS_PORT` defaults (`localhost:6379`) point at the embedded instance, so leave them alone. -**Data** is persisted to `/redis-data` inside the container. In the reference compose, that's mounted to a Docker volume (`romm_redis_data`) so queue state survives restarts. Don't skip the volume; you'll lose in-flight tasks on every `docker compose up -d`. +**Data** is persisted to `/redis-data` inside the container. In the reference compose, that's mounted to a Docker volume (`romm_redis_data`) so queue state survives restarts. Don't skip the volume, because you'll lose in-flight tasks on every `docker compose up -d`. ## Option B: external Redis container @@ -84,7 +84,7 @@ The full list of Redis env vars lives in [Environment Variables](../reference/en RomM's Redis usage is light: sessions, a queue, a bit of cache. Defaults are fine for anything up to tens of thousands of ROMs and a few dozen users. Things to know: - **`appendonly yes`** is strongly recommended (what the reference compose uses). Without it, a crash loses any task currently in the queue. -- **RDB snapshotting** is fine on top; `save 60 1` gives you a minutely snapshot. +- **RDB snapshotting** is fine on top. `save 60 1` gives you a minutely snapshot. - **Memory**: RomM doesn't hold large blobs in Redis. A 256 MB `maxmemory` is plenty for most instances. - **Key eviction**: leave `maxmemory-policy` alone (default: `noeviction`). RomM doesn't tolerate silent key loss: sessions would drop and running tasks would lose state. @@ -98,7 +98,7 @@ docker exec romm redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWD" If you see `PONG` from the RomM container, you're good. If not, check: - That the DNS name in `REDIS_HOST` resolves from the RomM container (use `docker exec romm getent hosts romm-redis`). -- That the password is correct; `redis-cli -a` will say `NOAUTH` if wrong. +- That the password is correct. `redis-cli -a` will say `NOAUTH` if wrong. - That `REDIS_SSL=true` matches whether the server actually requires TLS. -Debugging further: see the Redis line in `docker logs romm` at startup; RomM logs the connection target. +Debugging further: see the Redis line in `docker logs romm` at startup, where RomM logs the connection target. diff --git a/docs/install/reverse-proxy.md b/docs/install/reverse-proxy.md index c855107e..02f4502a 100644 --- a/docs/install/reverse-proxy.md +++ b/docs/install/reverse-proxy.md @@ -8,7 +8,7 @@ description: Put RomM behind Caddy, nginx, Traefik, or Nginx Proxy Manager with The RomM container listens on plain HTTP on port `8080`. For anything beyond `localhost` you should put it behind a reverse proxy that terminates TLS and forwards to the container. !!! tip "WebSockets are required" - RomM uses Socket.IO (both the general `/ws/socket.io` endpoint and the `/netplay/socket.io` endpoint) for live updates, scan progress, and Netplay. Every reverse-proxy recipe below keeps WebSocket support on; don't strip it out. + RomM uses Socket.IO (both the general `/ws/socket.io` endpoint and the `/netplay/socket.io` endpoint) for live updates, scan progress, and Netplay. Every reverse-proxy recipe below keeps WebSocket support on, so don't strip it out. The examples here assume your RomM container is reachable at `romm:8080` (by container name on a Docker network) or `192.168.1.100:8080` (by IP on the LAN). Swap to whatever's right for your setup. @@ -147,7 +147,7 @@ labels: ## Nginx Proxy Manager -Items marked ❗ are important; RomM won't work right without them. +Items marked ❗ are important. RomM won't work right without them. ### Details @@ -190,4 +190,4 @@ environment: - ROMM_BASE_URL=https://romm.mysite.com ``` -If you're also using OIDC, update `OIDC_REDIRECT_URI` to match; see [OIDC Setup](../administration/oidc/index.md). +If you're also using OIDC, update `OIDC_REDIRECT_URI` to match. See [OIDC Setup](../administration/oidc/index.md). diff --git a/docs/install/synology.md b/docs/install/synology.md index 0cddc64a..687b203b 100644 --- a/docs/install/synology.md +++ b/docs/install/synology.md @@ -31,7 +31,7 @@ mkdir -p /volume1/data/media/games/library/roms mkdir -p /volume1/data/media/games/library/bios ``` -The platform folder names inside `roms/` have to match RomM's conventions; see [Folder Structure](../getting-started/folder-structure.md). +The platform folder names inside `roms/` have to match RomM's conventions. See [Folder Structure](../getting-started/folder-structure.md). ### User uploads + config @@ -60,7 +60,7 @@ openssl rand -hex 32 # -> 03a054b6ca27e0107c5eed552ea66bacd9f3a2a8a91e7595cd462a593f9ecd09 ``` -Keep the output; it becomes `ROMM_AUTH_SECRET_KEY` in your compose file. Don't lose it; rotating invalidates every session and invite link. +Keep the output. It becomes `ROMM_AUTH_SECRET_KEY` in your compose file. Don't lose it, because rotating invalidates every session and invite link. ## 4. Set up metadata provider credentials @@ -69,7 +69,7 @@ Recommended before the first scan. Full walkthrough in [Metadata Providers](../a ## 5. Docker Compose !!! info "MariaDB 10.7 note" - This guide pins MariaDB to **10.7** for stability on older DSM versions. MariaDB 11 works on DSM 7.2+; bump the image tag if you like. + This guide pins MariaDB to **10.7** for stability on older DSM versions. MariaDB 11 works on DSM 7.2+, so bump the image tag if you like. The Synology-flavoured compose file: MariaDB on port `3309` externally (to avoid colliding with Synology's built-in MariaDB), UID/GID customisation, simplified healthcheck: @@ -98,7 +98,7 @@ Once RomM reports it's listening, open `http://:7676` in a browser. The ## Notes -- **Permissions**: make sure the UID/GID in your compose file has read-write on every host path you mounted. Synology's default `docker` user is often `1024:100`; the `apps` user is `568`. Pick one and be consistent. +- **Permissions**: make sure the UID/GID in your compose file has read-write on every host path you mounted. Synology's default `docker` user is often `1024:100`, and the `apps` user is `568`. Pick one and be consistent. - **HTTPS**: put Synology's built-in reverse proxy (Control Panel → Login Portal → Advanced → Reverse Proxy) in front of RomM, or use the [Reverse Proxy](reverse-proxy.md) recipes. - **Back up `/volume1/docker/romm` and your DB volume** before upgrading RomM versions. See [Backup & Restore](backup-and-restore.md). diff --git a/docs/install/truenas.md b/docs/install/truenas.md index 6ce0a873..4fab0cda 100644 --- a/docs/install/truenas.md +++ b/docs/install/truenas.md @@ -5,7 +5,7 @@ description: Install RomM on TrueNAS SCALE via the App Catalog or YAML. # TrueNAS -This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported; its FreeBSD jail system doesn't run Docker images. +This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported because its FreeBSD jail system doesn't run Docker images. ## Prerequisites @@ -23,9 +23,9 @@ This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported; its FreeBSD j ### 2. Fill in the install form -Step through the installation UI. You'll be asked for the same set of env vars as [Quick Start](../getting-started/quick-start.md); most defaults work. Things to look at: +Step through the installation UI. You'll be asked for the same set of env vars as [Quick Start](../getting-started/quick-start.md), and most defaults work. Things to look at: -- **Database credentials**: TrueNAS will offer to provision MariaDB for you; just pick a strong password. +- **Database credentials**: TrueNAS will offer to provision MariaDB for you. Just pick a strong password. - **`ROMM_AUTH_SECRET_KEY`**: generate via `openssl rand -hex 32` on any Linux box and paste the output. - **Metadata provider credentials**: fill in whatever you've registered for. See [Metadata Providers](../administration/metadata-providers.md). - **Storage configurations**: point the **Library** and **Assets** volumes at datasets you control. Make sure the UID/GID defined in the app config (default: `568`, the `apps` user) has ACL access to those datasets. @@ -57,7 +57,7 @@ Fill in the empty values with credentials you created in [Quick Start](../gettin ### 3. Install -Save. Same troubleshooting applies; see below. +Save. Same troubleshooting applies, see below. ## Troubleshooting @@ -81,4 +81,4 @@ In at least one reported setup, creating a user/group in TrueNAS with UID/GID `1 ## Contributing -Suggestions welcome; PRs against [rommapp/docs](https://github.com/rommapp/docs). +Suggestions welcome. PRs against [rommapp/docs](https://github.com/rommapp/docs). diff --git a/docs/install/unraid.md b/docs/install/unraid.md index 9aca26d6..4a1a63a2 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -37,7 +37,7 @@ From **Apps** → search `mariadb`. Only the [official `mariadb`](https://hub.do ![community apps search results for MariaDB](https://github.com/user-attachments/assets/76f4b6ef-5b63-454f-9357-d2920b9afd0e) -Fill in the env vars; names and sensible defaults live in the [reference `docker-compose.yml`](docker-compose.md). Set the network to **Custom: romm**. +Fill in the env vars. Names and sensible defaults live in the [reference `docker-compose.yml`](docker-compose.md). Set the network to **Custom: romm**. ![MariaDB environment variables](https://github.com/user-attachments/assets/a11906c5-25b2-46f1-906b-451a9ee39dca) diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index d890a7f7..74172f58 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -5,7 +5,7 @@ description: Add platforms RomM doesn't natively support, plus custom platform i # Custom Platforms -RomM ships with support for ~400 platforms. If yours isn't in [the list](supported-platforms.md), you can still load it as a custom platform; RomM just won't have metadata-provider coverage for it. +RomM ships with support for ~400 platforms. If yours isn't in [the list](supported-platforms.md), you can still load it as a custom platform. RomM just won't have metadata-provider coverage for it. ## Adding a custom platform @@ -27,7 +27,7 @@ Then either run a **Quick Scan** (the platform is auto-discovered) or trigger a ## Mapping to a canonical platform (preferred when possible) -If your platform is one RomM supports but under a different slug, you don't need a custom platform; just **remap**. Add to `config.yml`: +If your platform is one RomM supports but under a different slug, you don't need a custom platform. Just **remap**. Add to `config.yml`: ```yaml system: @@ -58,7 +58,7 @@ services: ### 2. Seed it with the official icons -RomM's built-in icons live at [`frontend/assets/platforms`](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms). Download them all and drop into your mounted directory; otherwise built-in platforms lose their icons too (because your mount overrides the whole directory). +RomM's built-in icons live at [`frontend/assets/platforms`](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms). Download them all and drop into your mounted directory, otherwise built-in platforms lose their icons too (because your mount overrides the whole directory). ### 3. Add your custom `.ico` files @@ -83,7 +83,7 @@ Same mechanic: just use a filename matching an existing platform's slug and your ## What custom platforms don't get - **Metadata provider coverage.** Providers are IGDB-slug-driven. A genuinely unknown platform won't have IGDB / ScreenScraper / MobyGames data. -- **EmulatorJS support.** The platform has to match a known EmulatorJS core; see [Supported Platforms → EmulatorJS column](supported-platforms.md). +- **EmulatorJS support.** The platform has to match a known EmulatorJS core. See [Supported Platforms → EmulatorJS column](supported-platforms.md). - **RetroAchievements.** Hash-based, but restricted to RA-supported platforms. For the niche platform case, you'll likely rely on filename-only matching (no provider) and browsing the library like a straight file system. diff --git a/docs/platforms/emulatorjs-config.md b/docs/platforms/emulatorjs-config.md index 9ee767c7..cb80d668 100644 --- a/docs/platforms/emulatorjs-config.md +++ b/docs/platforms/emulatorjs-config.md @@ -16,7 +16,7 @@ environment: - ENABLE_EMULATORJS=true # default ``` -Set to `false` to disable EmulatorJS entirely: useful on the slim image or when running headless with companion apps only. The Play button is hidden; Ruffle still works independently. See [Image Variants](../install/image-variants.md). +Set to `false` to disable EmulatorJS entirely: useful on the slim image or when running headless with companion apps only. The Play button is hidden, but Ruffle still works independently. See [Image Variants](../install/image-variants.md). ## Debug mode @@ -25,7 +25,7 @@ emulatorjs: debug: true ``` -Logs every available option for each core to the browser console when a game launches. Turn on when tuning; turn off in production (it's verbose). +Logs every available option for each core to the browser console when a game launches. Turn on when tuning, and turn off in production (it's verbose). ## Cache limit @@ -123,11 +123,11 @@ The structure: - Top level = core name. - Second level = player number (`0`, `1`, `2`, `3`). -- Third level = button slot (core-specific; 0 is usually the first face button). +- Third level = button slot (core-specific, and 0 is usually the first face button). - `value` = keyboard key. - `value2` = gamepad button. -Slot indexes and what they map to vary per core; see [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/). +Slot indexes and what they map to vary per core. See [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/). Users can override operator-level defaults in-game via Menu → **Controls**. Operator-level just sets the starting point. @@ -171,7 +171,7 @@ For frontend parity: - **Settings don't apply.** `debug: true`, reload a game, check console for "option set" logs. Core name might be wrong. - **Netplay breaks after enabling.** See [Netplay Troubleshooting](../troubleshooting/netplay.md). -- **Control map works in one game, not another.** Different cores; check you've set the right core name for the platform. +- **Control map works in one game, not another.** Different cores. Check you've set the right core name for the platform. ## See also diff --git a/docs/platforms/firmware-by-platform.md b/docs/platforms/firmware-by-platform.md index b5888632..190ae4d3 100644 --- a/docs/platforms/firmware-by-platform.md +++ b/docs/platforms/firmware-by-platform.md @@ -26,7 +26,7 @@ This page lists the firmware RomM's EmulatorJS cores need for each platform. For | **Intellivision** | `intellivision` | `exec.bin`, `grom.bin` | Bundle both. | | **3DO** | `3do` | `panafz10.bin` (or similar region BIOS) | | | **PC Engine CD / TurboGrafx-CD** | `pcecd` / `tg16cd` | `syscard3.pce` | | -| **Dreamcast** | `dc` | `dc_boot.bin`, `dc_flash.bin` | EmulatorJS Dreamcast support is limited; results vary. | +| **Dreamcast** | `dc` | `dc_boot.bin`, `dc_flash.bin` | EmulatorJS Dreamcast support is limited, so results vary. | | **Atari 5200** | `atari5200` | `5200.rom` | | | **Atari 7800** | `atari7800` | `7800 BIOS (U).rom` | | @@ -55,16 +55,16 @@ Common mistakes: - Upper vs lower case: most cores are case-sensitive. Use exact case from the table above. - Extensions: `.bin` vs `.rom` vs `.img` depend on the core. Don't rename. -- Version suffixes: `scph1001 (US) v4.4.bin` won't load; it has to be `scph1001.bin`. +- Version suffixes: `scph1001 (US) v4.4.bin` won't load. It has to be `scph1001.bin`. -If you're not sure what name a core expects, check the [upstream EmulatorJS systems docs](https://emulatorjs.org/docs/systems/) or load the game with `emulatorjs.debug: true`; the browser console will tell you what file it tried to load. +If you're not sure what name a core expects, check the [upstream EmulatorJS systems docs](https://emulatorjs.org/docs/systems/) or load the game with `emulatorjs.debug: true`. The browser console will tell you what file it tried to load. ## Getting firmware Your options (jurisdiction-dependent): - **Dump from real hardware you own.** The cleanest answer. -- **Community archives.** Not linked here; the RomM project doesn't host these and doesn't recommend specific sources. +- **Community archives.** Not linked here. The RomM project doesn't host these and doesn't recommend specific sources. - **Official emulator bundles.** Some modern emulators ship with (or include a button to download) BIOS files. If you bought one, it's yours to use. ## Verifying integrity diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md index 6b4ffbfd..6689a3e5 100644 --- a/docs/platforms/ms-dos.md +++ b/docs/platforms/ms-dos.md @@ -19,9 +19,9 @@ DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-p DOS games come in three flavours, each needs a different approach: -- **Homebrew:** indie / modern DOS games. Just an `.exe`; usually Just Works once mounted. +- **Homebrew:** indie / modern DOS games. Just an `.exe`, and usually Just Works once mounted. - **Shareware demos:** what most "free DOS games" sites distribute. Same as homebrew: all files in one folder. -- **Retail:** need the original CD mounted alongside the installed game files. More work; every game is different. +- **Retail:** need the original CD mounted alongside the installed game files. More work, and every game is different. ## The manual way (commandline) @@ -34,7 +34,7 @@ dir # find the .EXE filename.exe # launch ``` -Works for homebrew and shareware. Retail games usually fail here; they want a CD mounted somewhere. +Works for homebrew and shareware. Retail games usually fail here because they want a CD mounted somewhere. ## The automatic way (`.conf` autoload) @@ -189,7 +189,7 @@ Not supported: `dosbox-pure` works with EmulatorJS Netplay intermittently, not r - **Mouse lag.** Enable `autolock=true` in `[sdl]`. - **Wrong resolution / aspect.** Tweak `[render] aspect=` and `windowresolution=`. - **Audio crackle.** Lower `rate=22050` to `rate=11025` or raise the mixer blocksize. -- **Game runs too fast.** Most auto-cycle issues; set `cycles=fixed 4000` (or another number) explicitly. +- **Game runs too fast.** Most auto-cycle issues. Set `cycles=fixed 4000` (or another number) explicitly. - **Keyboard doesn't work for certain keys.** `usescancodes=false` sometimes fixes it. ## Dosemu / Exodos alternatives diff --git a/docs/platforms/ruffle-config.md b/docs/platforms/ruffle-config.md index 43409f6a..ad11dad9 100644 --- a/docs/platforms/ruffle-config.md +++ b/docs/platforms/ruffle-config.md @@ -38,9 +38,9 @@ Now `web-games/` is treated as the `flash` platform. See [Configuration File → ## Supported content -- **Flash (SWF):** 2D games, most work cleanly. AS1/AS2 excellent; AS3 still maturing in Ruffle. -- **Shockwave (DCR):** partial support; complex 3D Shockwave games often fail. -- **FLV / F4V:** Flash video; playable but not game-like. +- **Flash (SWF):** 2D games, most work cleanly. AS1/AS2 excellent, and AS3 still maturing in Ruffle. +- **Shockwave (DCR):** partial support. Complex 3D Shockwave games often fail. +- **FLV / F4V:** Flash video, playable but not game-like. [Ruffle compatibility database](https://ruffle.rs/#compatibility) has per-title status if you want to check a specific game. @@ -48,7 +48,7 @@ Now `web-games/` is treated as the `flash` platform. See [Configuration File → Ruffle honours the usual [RomM naming conventions](../getting-started/folder-structure.md#naming-convention): filename tags, regions, revisions. -No specific requirements beyond that; Ruffle reads the SWF directly. +No specific requirements beyond that. Ruffle reads the SWF directly. ## Saves @@ -77,7 +77,7 @@ On a handheld / Console Mode, Flash games are generally unplayable unless you've ## Version / updates -Ruffle is bundled in the full RomM image. Updates to Ruffle land when RomM updates its image; there's no separate Ruffle update knob. +Ruffle is bundled in the full RomM image. Updates to Ruffle land when RomM updates its image, and there's no separate Ruffle update knob. ## Not in 5.0 yet diff --git a/docs/platforms/supported-platforms.md b/docs/platforms/supported-platforms.md index 7951fd3f..369f9626 100644 --- a/docs/platforms/supported-platforms.md +++ b/docs/platforms/supported-platforms.md @@ -11,7 +11,7 @@ RomM ships support for ~400 platforms. "Support" means: 2. **At least one metadata provider** has coverage. 3. **EmulatorJS** may have a playable core (flagged per platform in the table below). -Your folder name has to match the **platform slug** in the table. If yours differs, use [`system.platforms`](../reference/configuration-file.md#systemplatforms) in `config.yml` to remap; see [Folder Structure](../getting-started/folder-structure.md). +Your folder name has to match the **platform slug** in the table. If yours differs, use [`system.platforms`](../reference/configuration-file.md#systemplatforms) in `config.yml` to remap. See [Folder Structure](../getting-started/folder-structure.md). ## Platform slugs + coverage diff --git a/docs/reference/configuration-file.md b/docs/reference/configuration-file.md index 851d1004..be7696bb 100644 --- a/docs/reference/configuration-file.md +++ b/docs/reference/configuration-file.md @@ -15,7 +15,7 @@ Start from the [`config.example.yml`](https://github.com/rommapp/romm/blob/maste - [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) !!! warning "Only set what you need" - Any omitted section uses the default. Don't copy the full example and then strip sections; just add what you want to change. + Any omitted section uses the default. Don't copy the full example and then strip sections. Just add what you want to change. --- @@ -25,7 +25,7 @@ Control what the scanner ignores. ### `exclude.platforms` -Skip entire platform folders. Values are platform slugs (not folder names; see [`system.platforms`](#systemplatforms) if your folders are named differently). +Skip entire platform folders. Values are platform slugs, not folder names. See [`system.platforms`](#systemplatforms) if your folders are named differently. ```yaml exclude: @@ -212,7 +212,7 @@ scan: ### `scan.priority.region` _(enhanced in 5.0)_ -Preferred region for titles, cover art, and regional variants. ScreenScraper uses this directly; other providers respect it where possible. +Preferred region for titles, cover art, and regional variants. ScreenScraper uses this directly, and other providers respect it where possible. **Default:** `["us", "wor", "ss", "eu", "jp"]` @@ -336,7 +336,7 @@ emulatorjs: ### `emulatorjs.netplay` _(new in 5.0)_ -Toggle Netplay and configure STUN/TURN servers. Google's public STUN servers are fine for most setups; run your own [coturn](https://github.com/coturn/coturn) or use [Metered's free tier](https://www.metered.ca/stun-turn) if you need TURN (symmetric NAT). +Toggle Netplay and configure STUN/TURN servers. Google's public STUN servers are fine for most setups. Run your own [coturn](https://github.com/coturn/coturn) or use [Metered's free tier](https://www.metered.ca/stun-turn) if you need TURN (symmetric NAT). ```yaml emulatorjs: @@ -398,7 +398,7 @@ See the [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/contr ## Editing via the UI -Everything above is also available from the Library Management page in the web UI; edits there write back to the same `config.yml`. Either path works; they're not separate stores. +Everything above is also available from the Library Management page in the web UI, and edits there write back to the same `config.yml`. Either path works. They're not separate stores. ## Per-file alternatives diff --git a/docs/reference/exports.md b/docs/reference/exports.md index 1b54e937..01c4c58d 100644 --- a/docs/reference/exports.md +++ b/docs/reference/exports.md @@ -135,7 +135,7 @@ For tight sync between RomM edits and the external frontend, run the export via ## Hash stability -Both export formats include some IDs (`igdb_id`, `moby_id`, etc.). If you rematch metadata, those IDs change. Don't point tools at the IDs as stable keys; use filenames / paths. +Both export formats include some IDs (`igdb_id`, `moby_id`, etc.). If you rematch metadata, those IDs change. Don't point tools at the IDs as stable keys. Use filenames / paths. ## Permissions diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md index 7dea77ca..e3a60474 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -33,7 +33,7 @@ For the pedagogical version with reasoning and examples, see [Core Concepts](../ **Device**: a registered endpoint that syncs with RomM. Tracked via [Device Sync Protocol](../ecosystem/device-sync-protocol.md). -**Editor**: mid-tier user role. Edit content (ROMs, platforms, collections), upload; no user management. See [Users & Roles](../administration/users-and-roles.md). +**Editor**: mid-tier user role. Edit content (ROMs, platforms, collections), upload, but no user management. See [Users & Roles](../administration/users-and-roles.md). **EmulatorJS**: the in-browser retro emulator RomM uses for most platforms. [In-Browser Play](../using/in-browser-play.md). @@ -63,7 +63,7 @@ For the pedagogical version with reasoning and examples, see [Core Concepts](../ **Library**: your ROM files on disk. Mounted as `/romm/library`. Platforms are subdirectories. [Folder Structure](../getting-started/folder-structure.md). -**LaunchBox**: metadata provider; uses a local downloaded DB. +**LaunchBox**: metadata provider. Uses a local downloaded DB. **Metadata provider**: external source of game data (IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, PlayMatch, LaunchBox, SteamGridDB, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro: 13 total in 5.0). [Metadata Providers](../administration/metadata-providers.md). @@ -127,7 +127,7 @@ For the pedagogical version with reasoning and examples, see [Core Concepts](../ **User**: an account. One of three roles (Viewer / Editor / Admin). -**Valkey**: open-source Redis fork; drop-in compatible. See [Redis or Valkey](../install/redis-or-valkey.md). +**Valkey**: open-source Redis fork, drop-in compatible. See [Redis or Valkey](../install/redis-or-valkey.md). **Viewer**: lowest user role. Read-only on library, own saves/states/profile. diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index 0dd285e0..35b0f443 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -12,7 +12,7 @@ Quick reference for the ports + URL paths a RomM instance exposes. | Port | Protocol | Purpose | Exposed? | | --- | --- | --- | --- | | `8080` | HTTP | nginx: the front door. Serves everything. | Yes, publish this. | -| `5000` | HTTP | gunicorn: FastAPI backend. Internal only. | No; nginx proxies here. | +| `5000` | HTTP | gunicorn: FastAPI backend. Internal only. | No. nginx proxies here. | | `6379` | TCP | Redis. Internal only (unless you externalise). | No. | Only **`8080`** should be reachable from outside the container in production. Typical compose `ports:`: @@ -48,7 +48,7 @@ Most of the RomM web UI: every page under `/`. Authenticated via browser cookie. ### Token-authenticated (bearer) -Every API endpoint under `/api/...` that requires a specific scope. Session cookies also work here; token auth is for scripts / companion apps. +Every API endpoint under `/api/...` that requires a specific scope. Session cookies also work here, but token auth is for scripts / companion apps. ### Download endpoints (optionally auth-off) @@ -63,7 +63,7 @@ See [Authentication → Download-endpoint auth bypass](../administration/authent | Path | Client | Auth | | --- | --- | --- | -| `/api/feeds/tinfoil` | [Tinfoil](../ecosystem/tinfoil.md) | Respects `DISABLE_DOWNLOAD_ENDPOINT_AUTH`; can send basic auth. | +| `/api/feeds/tinfoil` | [Tinfoil](../ecosystem/tinfoil.md) | Respects `DISABLE_DOWNLOAD_ENDPOINT_AUTH`, and can send basic auth. | | `/api/feeds/pkgi/...` | [pkgj](../ecosystem/pkgj.md) | Same. | | `/api/feeds/fpkgi/...` | [fpkgi](../ecosystem/fpkgi.md) | Same. | | `/api/feeds/kekatsu/...` | [Kekatsu](../ecosystem/kekatsu.md) | Same. | @@ -78,7 +78,7 @@ Full catalogue in [Feeds](feeds.md). | `/ws/socket.io` | General live updates (scans, tasks). | | `/netplay/socket.io` | Netplay session coordination. | -Both use Socket.IO; reverse proxy must pass through the upgrade. See [WebSockets](../developers/websockets.md) and [Reverse Proxy](../install/reverse-proxy.md). +Both use Socket.IO, so the reverse proxy must pass through the upgrade. See [WebSockets](../developers/websockets.md) and [Reverse Proxy](../install/reverse-proxy.md). ## Volumes (not ports but relevant) @@ -122,9 +122,9 @@ environment: - ROMM_BASE_PATH=/romm ``` -nginx inside the container rewrites paths internally; your reverse proxy forwards the full path. +nginx inside the container rewrites paths internally, and your reverse proxy forwards the full path. -Not the most common pattern; most deployments use a subdomain (`romm.example.com`) rather than a path. +Not the most common pattern. Most deployments use a subdomain (`romm.example.com`) rather than a path. ## See also diff --git a/docs/reference/scheduled-tasks.md b/docs/reference/scheduled-tasks.md index 7a3295a0..f744b0ff 100644 --- a/docs/reference/scheduled-tasks.md +++ b/docs/reference/scheduled-tasks.md @@ -38,7 +38,7 @@ Set the matching env var from the table above, restart the container, and the sc Two approaches: -- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`); cleanest. +- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`). Cleanest. - **Cron-it-off.** Set the cron to a moment that effectively never fires. `0 0 31 2 *` (Feb 31st) works. ## Where the table comes from diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md index eb92a240..960c0386 100644 --- a/docs/releases/changelog.md +++ b/docs/releases/changelog.md @@ -1,11 +1,11 @@ --- title: Changelog -description: Summary of notable RomM releases; authoritative per-release notes live on GitHub. +description: Summary of notable RomM releases. Authoritative per-release notes live on GitHub. --- # Changelog -This page summarises major RomM releases. **The authoritative per-release changelog lives on [GitHub Releases](https://github.com/rommapp/romm/releases)**; every tag there has full commit lists, breaking changes flagged, and upgrade notes. +This page summarises major RomM releases. **The authoritative per-release changelog lives on [GitHub Releases](https://github.com/rommapp/romm/releases)**. Every tag there has full commit lists, breaking changes flagged, and upgrade notes. For migration-grade detail on a major version, go straight to that version's guide: diff --git a/docs/releases/index.md b/docs/releases/index.md index 1fda789a..70d453e6 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -14,20 +14,20 @@ RomM's current stable is [`5.0.0`](changelog.md). Read [What's New in 5.0](../ge RomM follows **SemVer** for breaking changes and **CalVer-ish** for cadence: major versions are planned milestones, not scheduled. - **Patch releases** (`5.0.1`, `5.0.2`): bug fixes only. Safe to pull automatically. No migration. -- **Minor releases** (`5.1.0`, `5.2.0`): additive features. Schema may migrate automatically via Alembic; no action required beyond reading the release notes. +- **Minor releases** (`5.1.0`, `5.2.0`): additive features. Schema may migrate automatically via Alembic, but no action required beyond reading the release notes. - **Major releases** (`5.0.0`, `6.0.0`): breaking changes. Read the migration guide before upgrading. ## Image tags and what to pin | Tag | What it moves to | When to use | | --- | --- | --- | -| `rommapp/romm:latest` | Every new stable release | Dev / "I'll deal with it" setups. **Never pin production to `:latest`**; you'll ship untested upgrades. | +| `rommapp/romm:latest` | Every new stable release | Dev / "I'll deal with it" setups. **Never pin production to `:latest`** because you'll ship untested upgrades. | | `rommapp/romm:5.0.0` | Immutable, specific release | Production. Update deliberately by bumping the tag. | | `rommapp/romm:5` | Latest in the 5.x line | Middle ground: auto-minor-upgrades within a major. | | `rommapp/romm:develop` | Every push to `master` | Don't. | | `rommapp/romm:5.0.0-slim` | Same as `5.0.0` but without EmulatorJS/Ruffle | Headless / API-only deployments. See [Image Variants](../install/image-variants.md). | -Registries: Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`); same tags, same content. +Registries: Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`), same tags, same content. ## Docs versions @@ -35,7 +35,7 @@ The docs site is versioned via [mike](https://github.com/jimporter/mike). Every - `docs.romm.app/latest/` → 5.x (this is what you're reading). - `docs.romm.app/4.8/` → frozen 4.x docs. -- Older majors may remain accessible for a while; see the support window below. +- Older majors may remain accessible for a while. See the support window below. ## Support window @@ -53,7 +53,7 @@ Older frozen docs are retained for 12 months after the major's support window en ## Migration guides - **[Upgrading to 5.0](upgrading-to-5.0.md):** 4.x → 5.0. Required reading. -- **[Upgrading to 3.0](upgrading-to-3.0.md):** 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical; keep for 2.x migrators. +- **[Upgrading to 3.0](upgrading-to-3.0.md):** 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical, but kept for 2.x migrators. ## Where releases are announced diff --git a/docs/releases/upgrading-to-3.0.md b/docs/releases/upgrading-to-3.0.md index 296b44b8..f99d546f 100644 --- a/docs/releases/upgrading-to-3.0.md +++ b/docs/releases/upgrading-to-3.0.md @@ -8,13 +8,13 @@ description: Historical migration guide for 2.x → 3.0: SQLite drop, auth requi !!! info "This is a historical guide" RomM 3.0 shipped a while ago. If you're on 2.x, still use this page. If you're on 3.x / 4.x, see [Upgrading to 5.0](upgrading-to-5.0.md). -Version 3.0 introduced several breaking changes aimed at improving performance and unlocking new features (EmulatorJS, in-browser saves/states). Read this entire page before pulling the new image; skipping steps will make RomM inaccessible or unresponsive. +Version 3.0 introduced several breaking changes aimed at improving performance and unlocking new features (EmulatorJS, in-browser saves/states). Read this entire page before pulling the new image. Skipping steps will make RomM inaccessible or unresponsive. All the changes below are reflected in the example [docker-compose.yml](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml), which was significantly simplified in 3.0. ## Dropped SQLite support -SQLite was removed because of ongoing engineering issues; MariaDB is stabler and the only supported default. If you were on SQLite, RomM will auto-migrate your data to MariaDB, **but you need to make the following changes before upgrading**. +SQLite was removed because of ongoing engineering issues. MariaDB is stabler and the only supported default. If you were on SQLite, RomM will auto-migrate your data to MariaDB, **but you need to make the following changes before upgrading**. In your environment variables, set `ROMM_DB_DRIVER` to `mariadb` (or drop the variable entirely, it's no longer required) and add: @@ -42,7 +42,7 @@ We know this breaks the "unrestricted sharing" pattern some users relied on. See ## Redis is built in -The 2.x "experimental Redis" add-on container is gone; RomM ships with Redis internally. Remove the external Redis container and these environment variables: +The 2.x "experimental Redis" add-on container is gone. RomM ships with Redis internally. Remove the external Redis container and these environment variables: ```yaml # Remove: @@ -51,7 +51,7 @@ The 2.x "experimental Redis" add-on container is gone; RomM ships with Redis int # - REDIS_PORT ``` -If you want an external Redis instance anyway (recommended for production), keep `REDIS_HOST` / `REDIS_PORT`; see [Redis or Valkey](../install/redis-or-valkey.md). +If you want an external Redis instance anyway (recommended for production), keep `REDIS_HOST` / `REDIS_PORT`. See [Redis or Valkey](../install/redis-or-valkey.md). ## Configuration folder @@ -68,7 +68,7 @@ Updated example: [config.example.yml](https://github.com/rommapp/romm/blob/maste ## New `/romm/assets` volume -3.0 introduced save, state, and screenshot management. Uploads land in `/romm/assets` inside the container; mount a host path so they persist: +3.0 introduced save, state, and screenshot management. Uploads land in `/romm/assets` inside the container, so mount a host path so they persist: ```yaml volumes: @@ -79,9 +79,9 @@ Put this next to the folder you mount as `/romm/library` so all RomM-owned data ## After the upgrade -- Visit the web UI. You'll see a Setup Wizard if you had auth disabled previously; complete it to create the first admin. +- Visit the web UI. You'll see a Setup Wizard if you had auth disabled previously. Complete it to create the first admin. - Run a scan to re-match any games that lost metadata during the SQLite → MariaDB migration. -- Start using saves/states; they'll live under `/romm/assets` from now on. +- Start using saves/states. They'll live under `/romm/assets` from now on. ## Rollback diff --git a/docs/releases/upgrading-to-5.0.md b/docs/releases/upgrading-to-5.0.md index bf283d69..e24005f0 100644 --- a/docs/releases/upgrading-to-5.0.md +++ b/docs/releases/upgrading-to-5.0.md @@ -11,7 +11,7 @@ Migration guide for **RomM 4.x → 5.0**. If you're on 2.x or earlier, upgrade t 5.0 is a major release with schema migrations and a handful of breaking changes. Skipping the pre-flight checklist will lose data or break your instance. !!! note "This guide is finalised at 5.0 GA" - Some sections below reference exact env-var renames and schema changes that will be confirmed against the final 5.0.0 source tag. If you're reading this before 5.0 GA, treat it as a structural outline; the commands are correct, specific rename lists will be filled in as the release tag is cut. + Some sections below reference exact env-var renames and schema changes that will be confirmed against the final 5.0.0 source tag. If you're reading this before 5.0 GA, treat it as a structural outline. The commands are correct, specific rename lists will be filled in as the release tag is cut. ## What changes in 5.0 @@ -25,7 +25,7 @@ Migration guide for **RomM 4.x → 5.0**. If you're on 2.x or earlier, upgrade t ### Big new features -Highlights only; full list in [What's New in 5.0](../getting-started/what-is-new-in-5.md): +Highlights only. Full list in [What's New in 5.0](../getting-started/what-is-new-in-5.md): - Console Mode (`/console`, TV/gamepad UI). - Smart and Virtual Collections. @@ -90,7 +90,7 @@ Commit this to version control if you track your compose file. Saves guesswork o ### 5. Block access while you upgrade -Optional but recommended for shared instances; take RomM offline for ~5 minutes: +Optional but recommended for shared instances. Take RomM offline for ~5 minutes: ```sh # Return 503 at the reverse proxy while upgrading @@ -133,19 +133,19 @@ What to watch for: - **Alembic migrations running:** lines prefixed `INFO [alembic.runtime.migration]`. Takes anywhere from seconds (small libraries) to a couple minutes (very large ones). - **`Application startup complete.`** RomM is healthy. - **`Watcher started.`** Filesystem watcher up (if enabled). -- **ERROR lines:** bad; go to [Rollback](#rollback). +- **ERROR lines:** bad. Go to [Rollback](#rollback). ### 4. Smoke-test Don't trust the migration until you've verified: - Log in as an Admin. -- **Administration → Server Stats:** counts look reasonable; no massive loss. -- Open a platform with lots of games; check a few games have metadata. -- Open a game's details; Personal tab shows your rating / playtime if you had any. -- Check **Administration → Users**; your accounts are intact. -- Run a **Quick** scan; should complete cleanly in a few seconds. -- (If you had OIDC) log out, log in via OIDC; should pick up `OIDC_CLAIM_ROLES` if configured. +- **Administration → Server Stats:** counts look reasonable, no massive loss. +- Open a platform with lots of games and check a few games have metadata. +- Open a game's details. Personal tab shows your rating / playtime if you had any. +- Check **Administration → Users**. Your accounts are intact. +- Run a **Quick** scan. Should complete cleanly in a few seconds. +- (If you had OIDC) log out, log in via OIDC. Should pick up `OIDC_CLAIM_ROLES` if configured. ## Env var migration table @@ -170,7 +170,7 @@ Full list in [Environment Variables](../reference/environment-variables.md). The ## `config.yml` changes -`config.yml` gains new sections; everything else stays backwards compatible. +`config.yml` gains new sections, and everything else stays backwards compatible. - **`scan.region`:** region preference order (array). - **`scan.language`:** language preference order (array). @@ -193,7 +193,7 @@ The docs site restructured in 5.0. Every old URL redirects to its new home: - `/latest/Usage/UserManagement/` → `/latest/administration/users-and-roles/` - `/latest/OIDC-Guides/OIDC-Setup-With-Keycloak/` → `/latest/administration/oidc/keycloak/` -And so on; every page in the 4.x IA is covered. External links in forum posts, blog articles, issue threads, and Discord messages keep working. Internal bookmarks to `/latest///` hit a 301 to the new slug automatically. +And so on. Every page in the 4.x IA is covered. External links in forum posts, blog articles, issue threads, and Discord messages keep working. Internal bookmarks to `/latest///` hit a 301 to the new slug automatically. ## Rollback @@ -246,7 +246,7 @@ Open a bug report on [GitHub](https://github.com/rommapp/romm/issues) with: ### Migrations hang -Watch `docker logs -f romm`. If Alembic is still running, it's still running; large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: +Watch `docker logs -f romm`. If Alembic is still running, it's still running. Large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: - DB connection issues (`docker logs romm-db`). - Out-of-memory kills (`dmesg | grep -i oom`). @@ -266,7 +266,7 @@ See [OIDC Setup → Role mapping](../administration/oidc/index.md#role-mapping-5 ### Scheduled tasks stop firing -5.0 renamed several cron env vars. Check [Environment Variables](../reference/environment-variables.md) and update your env to the new names; otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). +5.0 renamed several cron env vars. Check [Environment Variables](../reference/environment-variables.md) and update your env to the new names, otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). ## After the upgrade diff --git a/docs/resources/snippets/scheduled-tasks.md b/docs/resources/snippets/scheduled-tasks.md index 1f5f98a7..22a62afd 100644 --- a/docs/resources/snippets/scheduled-tasks.md +++ b/docs/resources/snippets/scheduled-tasks.md @@ -12,4 +12,4 @@ | Cleanup Missing ROMs | Manual | `-` | `-` | Remove DB entries whose files are no longer on disk. | | Cleanup Orphaned Resources | Manual | `-` | `-` | Delete cached media not referenced by any ROM. | | Sync Folder Scan | Manual | `-` | `-` | On-demand library scan + sync. | -| Filesystem Watcher | Watcher | `-` | `WATCHER_ENABLED` | Live-watch the library folder; trigger quick scans on changes. | +| Filesystem Watcher | Watcher | `-` | `WATCHER_ENABLED` | Live-watch the library folder and trigger quick scans on changes. | diff --git a/docs/scripts/gen_platforms.py b/docs/scripts/gen_platforms.py index 539752e9..65f4c148 100644 --- a/docs/scripts/gen_platforms.py +++ b/docs/scripts/gen_platforms.py @@ -4,7 +4,7 @@ Strategy: fetch backend/utils/generate_supported_platforms.py from upstream along with the platform metadata it consumes, then exec it in a sandbox. -For now this is a stub that emits a placeholder snippet; the real exec +For now this is a stub that emits a placeholder snippet. The real exec needs a copy of the upstream utility's input data layout, which we'll wire up after pinning a 5.0 SHA in sources.toml. diff --git a/docs/scripts/gen_scheduled_tasks.py b/docs/scripts/gen_scheduled_tasks.py index 2f8669ad..88cca18b 100644 --- a/docs/scripts/gen_scheduled_tasks.py +++ b/docs/scripts/gen_scheduled_tasks.py @@ -4,7 +4,7 @@ Strategy: parse the upstream task registration calls (or, once a stable public registry exists, import it). For now we ship a hand-curated table -to unblock Wave 1; real auto-extraction lands once the upstream registry +to unblock Wave 1. Real auto-extraction lands once the upstream registry shape stabilizes for 5.0. Run manually: @@ -94,7 +94,7 @@ "type": "Watcher", "default_cron": "-", "env_var": "WATCHER_ENABLED", - "purpose": "Live-watch the library folder; trigger quick scans on changes.", + "purpose": "Live-watch the library folder and trigger quick scans on changes.", }, ] diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index ba547080..5a7397b8 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -18,11 +18,11 @@ Fix: [clear cookies](https://support.google.com/accounts/answer/32050) for the R CSRF protection is on by default. A mismatched or missing `csrftoken` cookie causes this. -1. Reload the page; RomM sets a fresh CSRF cookie on GET requests, which should fix it on the next POST. +1. Reload the page. RomM sets a fresh CSRF cookie on GET requests, which should fix it on the next POST. 2. Still broken? Clear cookies for the RomM host and hard-reload (`CMD+SHIFT+R` / `CTRL+F5`). -3. Known to happen on Chrome specifically; rare on Firefox/Safari. +3. Known to happen on Chrome specifically, and rare on Firefox/Safari. -If you're behind a reverse proxy and CSRF keeps failing, the proxy is probably stripping the `csrftoken` cookie or the `X-CSRFToken` header. See the [Reverse Proxy recipes](../install/reverse-proxy.md); every one of them forwards `Cookie` and all custom headers by default, so if yours doesn't, fix the proxy config. +If you're behind a reverse proxy and CSRF keeps failing, the proxy is probably stripping the `csrftoken` cookie or the `X-CSRFToken` header. See the [Reverse Proxy recipes](../install/reverse-proxy.md). Every one of them forwards `Cookie` and all custom headers by default, so if yours doesn't, fix the proxy config. ## `400 Bad Request` on the WebSocket endpoint @@ -30,7 +30,7 @@ Your reverse proxy is stripping the WebSocket upgrade. RomM uses Socket.IO for l Fixes per proxy: -- **Nginx / NPM**: enable WebSockets Support; the [Reverse Proxy](../install/reverse-proxy.md) snippets already do this. +- **Nginx / NPM**: enable WebSockets Support. The [Reverse Proxy](../install/reverse-proxy.md) snippets already do this. - **Traefik**: add `proxy_set_header Upgrade $http_upgrade` (or use the Traefik middleware equivalent). - **Caddy**: WebSockets work out of the box with `reverse_proxy`. - **Cloudflare**: enable **WebSockets** under Network settings. @@ -41,7 +41,7 @@ IGDB creds are wrong or revoked on the Twitch side. 1. Go to [dev.twitch.tv/console/apps](https://dev.twitch.tv/console/apps). 2. Verify your RomM application still exists. -3. Regenerate the Client Secret; copy both Client ID and Client Secret. +3. Regenerate the Client Secret, and copy both Client ID and Client Secret. 4. Update `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` in your RomM env. 5. `docker compose up -d` to pick up the new values. @@ -52,7 +52,7 @@ You set `DISABLE_USERPASS_LOGIN=true` and now OIDC isn't working. 1. Edit your compose / env to unset `DISABLE_USERPASS_LOGIN` (or set it to `false`). 2. `docker compose up -d` to restart with the new config. 3. Log in with your local admin. -4. Fix OIDC; see below. +4. Fix OIDC. See below. 5. Re-enable `DISABLE_USERPASS_LOGIN` only after confirming OIDC works end-to-end. This is the reason [OIDC Setup](../administration/oidc/index.md) tells you to verify OIDC before turning off local login. @@ -80,7 +80,7 @@ You configured `OIDC_CLAIM_ROLES` but RomM isn't honouring it. 2. **Does the value match?** `OIDC_ROLE_ADMIN=romm-admin` will only match if the claim contains exactly the string `romm-admin`. Case-sensitive. 3. **Is the claim mapper on the IdP side configured to include the claim?** On Keycloak, for example, you need a Client Scope with a Group Membership mapper added to the client. -Roles are re-evaluated on every login; there's no cache to bust. Log out and back in after fixing. +Roles are re-evaluated on every login, and there's no cache to bust. Log out and back in after fixing. ### "Email is missing from token" (Zitadel-specific) @@ -120,5 +120,5 @@ If `bypass_autologin` doesn't work in your version, shell into the container and ## Still stuck - Check the container logs: `docker logs romm 2>&1 | grep -iE 'auth|oidc|oauth'`. -- Cross-reference your IdP's audit logs; they often show exactly why a login was rejected on their side. +- Cross-reference your IdP's audit logs. They often show exactly why a login was rejected on their side. - Ask on [Discord](https://discord.gg/romm) `#help` with the IdP name and the exact error text. diff --git a/docs/troubleshooting/in-browser-play.md b/docs/troubleshooting/in-browser-play.md index c35526c1..174f17ac 100644 --- a/docs/troubleshooting/in-browser-play.md +++ b/docs/troubleshooting/in-browser-play.md @@ -13,7 +13,7 @@ Checks in order: 1. **Is it the slim image?** `ENABLE_EMULATORJS=true` on the slim image doesn't magically include EmulatorJS. Either switch to `rommapp/romm:` (full) or disable in-browser play. See [Image Variants](../install/image-variants.md). 2. **Browser console**: open devtools, look for 404s on `/assets/emulatorjs/...`. Indicates the EmulatorJS bundle didn't install correctly in the container. `docker logs romm` may show the entrypoint's install step failing. -3. **Browser compatibility**: EmulatorJS uses SharedArrayBuffer. Needs a modern Chrome/Firefox/Safari and an HTTPS-served RomM (cross-origin isolation requires HTTPS). If you're still on plain HTTP for some reason, TLS it; see [Reverse Proxy](../install/reverse-proxy.md). +3. **Browser compatibility**: EmulatorJS uses SharedArrayBuffer. Needs a modern Chrome/Firefox/Safari and an HTTPS-served RomM (cross-origin isolation requires HTTPS). If you're still on plain HTTP for some reason, TLS it. See [Reverse Proxy](../install/reverse-proxy.md). ## Black screen, no audio @@ -50,7 +50,7 @@ Multi-file firmware: zip them together and upload as a single `firmware.zip`. Em - **Underpowered device.** Cheap TV boxes and old phones struggle. Try a lighter core (e.g. `snes9x` instead of `bsnes`), or accept the stutter. - **Tab is backgrounded.** Most browsers throttle background tabs. Keep the play tab foregrounded. - **Other tabs eating CPU.** Close them. -- **Specific to a core.** `mednafen_saturn` is notorious; try `yabause` if Saturn audio is bad. +- **Specific to a core.** `mednafen_saturn` is notorious. Try `yabause` if Saturn audio is bad. ## Save states crash on load @@ -69,7 +69,7 @@ Rebind via Profile → User Interface (the operator-side overrides live in [`emu ## DOS games fail to boot - **autorun.bat issues**: turn on [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjsdisable_batch_bootup) in `config.yml`. -- **Sound card config wrong**: dosbox-pure tries to auto-detect; may need manual tweaking. In-game Menu → **Settings** → set Sound Blaster port. +- **Sound card config wrong**: dosbox-pure tries to auto-detect, but may need manual tweaking. In-game Menu → **Settings** → set Sound Blaster port. - **Game needs specific CPU speed**: some DOS games are CPU-bound. Slow down via dosbox-pure settings. See the [MS-DOS platform guide](../platforms/ms-dos.md) for deeper DOS-specific notes. @@ -100,7 +100,7 @@ In-browser emulation is CPU-heavy. Mobile tips: | Core | Known issues | | --- | --- | | `ppsspp` (PSP) | Not supported in Console Mode. | -| `dosbox-pure` (DOS) | Not supported in Console Mode. Some DOS quirks; see `disable_batch_bootup`. | +| `dosbox-pure` (DOS) | Not supported in Console Mode. Some DOS quirks. See `disable_batch_bootup`. | | `mednafen_saturn` | Audio stutter on weak hardware. `yabause` is an alternative. | | `opera` (3DO) | Niche. Limited ROM compatibility. | | All Nintendo DS cores | Touchscreen emulation is imperfect on non-touch devices. | diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md index 06bffcec..45d5800f 100644 --- a/docs/troubleshooting/index.md +++ b/docs/troubleshooting/index.md @@ -11,9 +11,9 @@ Something's broken. Start with the symptom that best matches: ### RomM won't start -- Container crashes immediately → check `docker logs romm`. If it's `invalid host in "tcp://..."`, you're on Kubernetes; see [Kubernetes Troubleshooting](kubernetes.md). +- Container crashes immediately → check `docker logs romm`. If it's `invalid host in "tcp://..."`, you're on Kubernetes. See [Kubernetes Troubleshooting](kubernetes.md). - Database connection errors → verify `DB_HOST` / `DB_PASSWD` match your DB container, and that the DB has finished initialising (first run takes longer than you'd think). -- "Page not found" on first load → wait; initial migrations and resource seeding take a minute. +- "Page not found" on first load → wait, because initial migrations and resource seeding take a minute. ### Login issues @@ -44,6 +44,6 @@ Something's broken. Start with the symptom that best matches: ## Still stuck -- [GitHub issues](https://github.com/rommapp/romm/issues): search first; open if you've got a reproducible bug. +- [GitHub issues](https://github.com/rommapp/romm/issues): search first, then open if you've got a reproducible bug. - [Discord](https://discord.gg/romm): `#help` channel, staffed by community + maintainers. - Logs to include when asking for help: `docker logs romm` (redact secrets), your RomM version (top of the About modal in the profile drawer), and the exact steps you took. diff --git a/docs/troubleshooting/kubernetes.md b/docs/troubleshooting/kubernetes.md index 96c09d85..c4b13cb9 100644 --- a/docs/troubleshooting/kubernetes.md +++ b/docs/troubleshooting/kubernetes.md @@ -88,7 +88,7 @@ Two fixes: Startup ordering. RomM starts before the DB is ready, fails, and crashlooped-restarts forever because the restart is too fast for the DB to catch up. -Fix: add an init container that waits, or a `readinessProbe` + generous `startupProbe` on the DB StatefulSet. The [Kubernetes install guide](../install/kubernetes.md#mariadb) has a readiness probe baked in; check you're using it. +Fix: add an init container that waits, or a `readinessProbe` + generous `startupProbe` on the DB StatefulSet. The [Kubernetes install guide](../install/kubernetes.md#mariadb) has a readiness probe baked in, so check you're using it. ## Scheduler tasks don't run @@ -117,7 +117,7 @@ resources: memory: "4Gi" ``` -Or disable hashing on the Scan page to cut memory use by ~80% (you lose RetroAchievements + Hasheous matching; see [Metadata Providers](../administration/metadata-providers.md)). +Or disable hashing on the Scan page to cut memory use by ~80% (you lose RetroAchievements + Hasheous matching, see [Metadata Providers](../administration/metadata-providers.md)). ## Still stuck diff --git a/docs/troubleshooting/miscellaneous.md b/docs/troubleshooting/miscellaneous.md index e6169600..bae1efb0 100644 --- a/docs/troubleshooting/miscellaneous.md +++ b/docs/troubleshooting/miscellaneous.md @@ -18,7 +18,7 @@ volumes: This only applies to SQLite. MariaDB / MySQL / Postgres users persist via the DB container's own volume (`mysql_data:/var/lib/mysql` and similar), not via a `/romm/database` mount. -For anything multi-user or larger than a few hundred games, **don't use SQLite**; see [Databases](../install/databases.md). +For anything multi-user or larger than a few hundred games, **don't use SQLite**. See [Databases](../install/databases.md). ## `Could not get twitch auth token: check client_id and client_secret` @@ -78,7 +78,7 @@ Static media is served from `/romm/resources` through nginx. If images 404: Most common causes, in order: 1. **Hashing large files on spinning disks.** Check **Skip hash calculation** or move the library to SSD. -2. **SQLite with many users.** Switch to MariaDB or Postgres; see [Databases](../install/databases.md). +2. **SQLite with many users.** Switch to MariaDB or Postgres. See [Databases](../install/databases.md). 3. **Metadata provider rate limiting during scans.** Reduce `SCAN_WORKERS` or split scans by platform. 4. **Container on underpowered hardware.** Check CPU/RAM headroom with `docker stats romm`. If RAM is pegged, raise container limits or disable `WATCHER_ENABLED`. @@ -88,7 +88,7 @@ Most common causes, in order: 2. Restore your DB dump from [Backup & Restore](../install/backup-and-restore.md). 3. Open a bug report with: old version, new version, the exact error or behaviour, and `docker logs romm` from the failed startup. -For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md); the migration guide covers every known breaking change. +For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5.0.md). The migration guide covers every known breaking change. ## It's not listed here diff --git a/docs/troubleshooting/netplay.md b/docs/troubleshooting/netplay.md index 2886a46c..6530b78e 100644 --- a/docs/troubleshooting/netplay.md +++ b/docs/troubleshooting/netplay.md @@ -13,15 +13,15 @@ The most common error. Almost always the operator-side config: 1. **Is Netplay enabled?** `emulatorjs.netplay.enabled: true` in `config.yml`. 2. **Are ICE servers configured?** The operator needs at least one STUN server in `emulatorjs.netplay.ice_servers`. Without any, NAT traversal can't begin. -3. **ICE server URLs reachable?** RomM can't talk to `stun.l.google.com:19302` if your server has no outbound internet; sounds silly but happens in air-gapped labs. +3. **ICE server URLs reachable?** RomM can't talk to `stun.l.google.com:19302` if your server has no outbound internet. Sounds silly but happens in air-gapped labs. -Operator: check `docker logs romm | grep -i netplay`; errors usually point at ICE config or network access. +Operator: check `docker logs romm | grep -i netplay`. Errors usually point at ICE config or network access. Full config: [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50). ## Can't see the room -You created a room as host; other players don't see it. +You created a room as host, but other players don't see it. - **They need RomM accounts on your instance.** Netplay doesn't federate. A user with no account or on a different RomM can't see or join. - **WebSocket connection is broken.** Open devtools → Network → WS tab. If Socket.IO is disconnecting, see [Authentication Troubleshooting → WebSockets](authentication.md#400-bad-request-on-the-websocket-endpoint). @@ -29,8 +29,8 @@ You created a room as host; other players don't see it. ## Joined but video never appears -- **Host's browser stopped streaming.** Check host's tab is still foregrounded; browsers throttle background tabs. -- **NAT traversal failed.** STUN couldn't hole-punch, and you have no TURN configured. Add a TURN server; see [Netplay → ICE servers](../using/netplay.md#ice-servers-the-nat-stuff). +- **Host's browser stopped streaming.** Check host's tab is still foregrounded, because browsers throttle background tabs. +- **NAT traversal failed.** STUN couldn't hole-punch, and you have no TURN configured. Add a TURN server. See [Netplay → ICE servers](../using/netplay.md#ice-servers-the-nat-stuff). - **Symmetric NAT on one end.** Corporate networks and some carrier-grade NAT can't be STUN-traversed. Only TURN (relay) will work. ## High lag / input delay @@ -38,7 +38,7 @@ You created a room as host; other players don't see it. Normal Netplay delay is 50–150 ms. More than that: - **You're routing through TURN.** TURN relays add latency proportional to your distance from the TURN server. Pick a TURN server geographically close to both players, or set up your own [coturn](https://github.com/coturn/coturn). -- **Host has slow uplink.** The host is streaming video to everyone; if upload bandwidth is tight, lag spikes. Test host's upload with `speedtest-cli`. +- **Host has slow uplink.** The host is streaming video to everyone, so if upload bandwidth is tight, lag spikes. Test host's upload with `speedtest-cli`. - **Weak host CPU.** The host runs the emulator AND encodes the video stream. A Raspberry Pi hosting N64 Netplay is going to struggle. ## Desync (players see different game state) @@ -47,7 +47,7 @@ Different players' screens diverge over time: you do a move, they don't see it. - **Browser tab backgrounded.** Browsers throttle. Keep both tabs foregrounded. - **Bandwidth starvation.** Video stream is dropping frames, inputs are queueing. Reduce resolution or lower the emulator frame rate. -- **Core-specific bug.** Not all cores handle Netplay cleanly. `snes9x` and `genesis_plus_gx` are most reliable; some 64-bit era cores have known desync bugs. +- **Core-specific bug.** Not all cores handle Netplay cleanly. `snes9x` and `genesis_plus_gx` are most reliable, but some 64-bit era cores have known desync bugs. ## Audio crackle / cuts out on the client side @@ -55,7 +55,7 @@ WebRTC audio is known to be fragile. Workarounds: - **Use Chrome.** Its WebRTC implementation is the most mature. - **Lower audio quality** in the in-game Menu → Audio. -- **Restart the session**; audio sometimes recovers only after a full room teardown. +- **Restart the session**. Audio sometimes recovers only after a full room teardown. ## Room disappears after the host leaves diff --git a/docs/troubleshooting/scanning.md b/docs/troubleshooting/scanning.md index d01359fc..cdb088a3 100644 --- a/docs/troubleshooting/scanning.md +++ b/docs/troubleshooting/scanning.md @@ -11,9 +11,9 @@ Most scan problems boil down to: library not mounted where RomM expects, folder Three common causes: -1. **Library mounted in the wrong place.** RomM expects your library at `/romm/library` inside the container. Verify with `docker exec romm ls /romm/library`; you should see your `roms/` directory (Structure A) or platform folders (Structure B). +1. **Library mounted in the wrong place.** RomM expects your library at `/romm/library` inside the container. Verify with `docker exec romm ls /romm/library`. You should see your `roms/` directory (Structure A) or platform folders (Structure B). 2. **Permissions.** The RomM container has to read the files. Check from the host: `ls -lh /path/to/library`. If the RomM process can't see them, nothing scans. -3. **Invalid folder structure.** Review [Folder Structure](../getting-started/folder-structure.md). RomM tries Structure A first; if it finds neither layout, the scan completes in seconds with nothing found. +3. **Invalid folder structure.** Review [Folder Structure](../getting-started/folder-structure.md). RomM tries Structure A first, so if it finds neither layout, the scan completes in seconds with nothing found. ## "ROMs not found for platform X, check romm folder structure" @@ -103,7 +103,7 @@ Check the scan log for per-provider errors: - **IGDB: "Could not get twitch auth token"** → `IGDB_CLIENT_ID` or `IGDB_CLIENT_SECRET` is wrong, or revoked on the Twitch side. - **ScreenScraper: "403 / DAILY_LIMIT_REACHED"** → you've hit the free-tier quota. Wait, or upgrade your ScreenScraper membership. - **RetroAchievements: "Invalid API key"** → regenerate at [retroachievements.org/settings](https://retroachievements.org/settings). -- **Any provider: "Request timed out"** → transient; re-run an **Unmatched** scan to retry only the failures. +- **Any provider: "Request timed out"** → transient. Re-run an **Unmatched** scan to retry only the failures. Full provider reference: [Metadata Providers](../administration/metadata-providers.md). @@ -125,4 +125,4 @@ Hashing large ROMs (PS1, Saturn, DC images) is IO-bound. Options: ## Scan seems to work but nothing shows up -Refresh the page; the ribbons on the home dashboard cache aggressively during a scan. Still blank? Check **Administration → Server Stats**; if counts are zero, the scan didn't actually persist anything. Usually a DB permission issue; check `docker logs romm 2>&1 | grep -i 'database'`. +Refresh the page. The ribbons on the home dashboard cache aggressively during a scan. Still blank? Check **Administration → Server Stats**. If counts are zero, the scan didn't actually persist anything. Usually a DB permission issue, so check `docker logs romm 2>&1 | grep -i 'database'`. diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md index 2924d048..e17f1b0b 100644 --- a/docs/troubleshooting/sync.md +++ b/docs/troubleshooting/sync.md @@ -17,7 +17,7 @@ Troubleshooting paths differ. ### App says "token invalid" - **Token revoked.** Check Profile → Client API Tokens. Was it deleted? -- **Token expired.** Tokens can have optional expiry; check the expiry date on the token page. +- **Token expired.** Tokens can have optional expiry. Check the expiry date on the token page. - **Scopes mismatch.** App needs scopes the token doesn't carry. Create a new token with the required scopes. See [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix). - **User downgraded.** If the owning user got demoted (Admin to Viewer, say), tokens with scopes outside the new role stop working. @@ -25,7 +25,7 @@ Troubleshooting paths differ. - **URL wrong.** Double-check what you entered in the app config. `https://romm.example.com`, not `https://romm.example.com/api` (the app appends the API path). - **TLS cert issues.** Self-signed certs cause problems. Use a proper cert (Let's Encrypt through your reverse proxy) or configure the app to skip cert validation (not recommended). -- **Firewall.** From the device, `curl https://romm.example.com/api/heartbeat`; should return JSON. If not, network path is blocked. +- **Firewall.** From the device, `curl https://romm.example.com/api/heartbeat`. Should return JSON. If not, network path is blocked. ### Pairing code doesn't work @@ -42,7 +42,7 @@ Full flow: [Client API Tokens](../ecosystem/client-api-tokens.md). - **Sync hasn't run yet.** The Push-Pull Device Sync task runs every 15 minutes by default. Check Administration → Tasks for last-run timestamp. - **App didn't upload.** Open the app's logs. Some apps upload on app-close rather than on every save. -- **Wrong save location on device.** RomM reads from a specific path; if the app stored saves elsewhere, they won't sync. Check the device's save path configuration. +- **Wrong save location on device.** RomM reads from a specific path. If the app stored saves elsewhere, they won't sync. Check the device's save path configuration. ## SSH-based sync (handhelds) @@ -80,12 +80,12 @@ OpenSSH refuses to use keys with loose permissions. On the host: chmod 600 /path/to/romm-sync-key ``` -And verify the Docker bind mount isn't forcing different perms (ro is fine; mode-reset-via-mount-option is the usual culprit). +And verify the Docker bind mount isn't forcing different perms (ro is fine, but mode-reset-via-mount-option is the usual culprit). ### Sync task doesn't run - **`SSH_PRIVATE_KEY_PATH` not set.** Check the RomM container's env. If the path doesn't exist inside the container, sync quietly no-ops. -- **No devices registered.** Administration → Devices; if empty, there's nothing to sync. +- **No devices registered.** Administration → Devices. If empty, there's nothing to sync. - **Cron expression wrong.** Default `PUSH_PULL_SYNC_INTERVAL_CRON=*/15 * * * *`. Invalid cron silently doesn't run. - **Task failing silently.** `GET /api/tasks/status`: status shows per-task state. "failed" with a last-error is a direct pointer. @@ -94,8 +94,8 @@ And verify the Docker bind mount isn't forcing different perms (ro is fine; mode Play sessions from a companion app (time-played tracking, Continue Playing ribbon): - **Not appearing in RomM**: app isn't POSTing to `/api/play-sessions`. Check app's logs. -- **Duplicate sessions**: app is re-posting on sync. Known edge case for some companion apps; usually harmless. -- **Times look wrong**: timezone mismatch between device and RomM. RomM stores UTC; display converts to user TZ. +- **Duplicate sessions**: app is re-posting on sync. Known edge case for some companion apps, usually harmless. +- **Times look wrong**: timezone mismatch between device and RomM. RomM stores UTC, and display converts to user TZ. ## Devices panel says "offline" @@ -103,7 +103,7 @@ RomM sees no heartbeat from the device. Either: - Device is genuinely off. - Device's companion app has stopped phoning home. Check the app's logs / relaunch it. -- RomM last-seen threshold elapsed. Devices show online for 1 hour after last heartbeat; after that, offline. +- RomM last-seen threshold elapsed. Devices show online for 1 hour after last heartbeat. After that, offline. ## Smart collection / filter isn't updating after sync diff --git a/docs/troubleshooting/synology.md b/docs/troubleshooting/synology.md index 29681d8f..c744ccdb 100644 --- a/docs/troubleshooting/synology.md +++ b/docs/troubleshooting/synology.md @@ -9,7 +9,7 @@ description: Fix DSM-specific permission and Docker issues. The usual Synology permission issue. Fix via SSH: -1. **Enable SSH** on the NAS if you haven't; see the [DSM guide](https://kb.synology.com/en-uk/DSM/tutorial/How_to_login_to_DSM_with_root_permission_via_SSH_Telnet). +1. **Enable SSH** on the NAS if you haven't. See the [DSM guide](https://kb.synology.com/en-uk/DSM/tutorial/How_to_login_to_DSM_with_root_permission_via_SSH_Telnet). 2. **SSH in** with your DSM admin account (same credentials as the DSM web UI). 3. **Find your UID/GID**: type `id` and note `uid=NNNN(user) gid=NNNN(group)`. 4. **Fix permissions on every RomM host path**: @@ -40,7 +40,7 @@ Credit: the permission-mode string comes from [DrFrankenstein's Docker user guid Synology ships its own MariaDB on port `3306`. If you try to run RomM's MariaDB container on the same port, one of them won't bind. -Fix: map MariaDB to a different host port in your compose file (the Synology install guide uses `3309:3306`); see [Synology install guide](../install/synology.md). +Fix: map MariaDB to a different host port in your compose file (the Synology install guide uses `3309:3306`). See [Synology install guide](../install/synology.md). ## RomM "Page not found" on first open @@ -58,7 +58,7 @@ DSM occasionally rotates Docker's data directory or the BTRFS subvolume paths af Fix: -1. Don't panic; your host-mounted paths (`/volume1/...`) aren't touched. +1. Don't panic. Your host-mounted paths (`/volume1/...`) aren't touched. 2. Check your compose file. Any volumes declared as *named* Docker volumes (as opposed to host bind mounts) might have been recreated empty. 3. Prefer host bind mounts on Synology specifically: `/volume1/docker/romm/resources` instead of a named `romm_resources` volume. The [Synology install guide](../install/synology.md) uses this pattern. diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index d52ff370..b3ac03b4 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -7,7 +7,7 @@ description: Manage your RomM account: profile, password, avatar, API tokens, de Your account controls: profile drawer → **Profile**. -Every user (Viewer, Editor, Admin) can manage their own profile. Admins can edit *other* users' profiles; see [Users & Roles](../administration/users-and-roles.md) for the admin side. +Every user (Viewer, Editor, Admin) can manage their own profile. Admins can edit *other* users' profiles. See [Users & Roles](../administration/users-and-roles.md) for the admin side. ## Profile basics @@ -16,13 +16,13 @@ Every user (Viewer, Editor, Admin) can manage their own profile. Admins can edit Editable from the profile page. Changing your email: - **OIDC users**: email is usually governed by the IdP. RomM lets you change it here but OIDC login will re-match on the IdP-supplied email at next login. -- **Local users**: change freely. The new email is used for password reset (if email transport is configured; see [Authentication](../administration/authentication.md)). +- **Local users**: change freely. The new email is used for password reset (if email transport is configured, see [Authentication](../administration/authentication.md)). ### Password -For local accounts: **Current password** + **New password** (twice). RomM uses bcrypt: no plaintext storage, no password recovery by the admin (admins reset by setting a new password; they can't see your old one). +For local accounts: **Current password** + **New password** (twice). RomM uses bcrypt: no plaintext storage, no password recovery by the admin (admins reset by setting a new password, but they can't see your old one). -OIDC users don't have local passwords; authentication is via the IdP. Password fields are hidden on the profile page. +OIDC users don't have local passwords, because authentication is via the IdP. Password fields are hidden on the profile page. ### Avatar @@ -36,17 +36,17 @@ If you're an OIDC user and want RomM to show your `preferred_username` from the Long-lived API tokens for companion apps, scripts, and integrations. Each token is scoped to a subset of your user's scopes, and optionally expires. -See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md); this section is the "how I create one from the UI" version. +See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md). This section is the "how I create one from the UI" version. ### Creating a token 1. **Profile → Client API Tokens → + New Token**. 2. Pick: - **Name**: descriptive (e.g. "Grout on my RG35XX"). - - **Scopes**: which permissions to include. Default is read-only; tick write scopes deliberately. + - **Scopes**: which permissions to include. Default is read-only. Tick write scopes deliberately. - **Expiry**: optional. Blank = never expires. 3. **Create**. -4. The token appears **once**. Copy it immediately; you can't retrieve it later. +4. The token appears **once**. Copy it immediately, because you can't retrieve it later. Token format: `rmm_` + 40 hex chars. Treat it like a password. @@ -66,7 +66,7 @@ Full flow in [Client API Tokens](../ecosystem/client-api-tokens.md). ### Limits - **25 active tokens per user.** Delete old ones to free slots. -- **Tokens carry a subset of your scopes.** If an admin demotes you to Viewer, any token you hold with admin scopes stops working; tokens don't escalate privileges beyond the owning user's current role. +- **Tokens carry a subset of your scopes.** If an admin demotes you to Viewer, any token you hold with admin scopes stops working. Tokens don't escalate privileges beyond the owning user's current role. ### Revoking @@ -101,7 +101,7 @@ Returns your user record plus a `personal_data` array of per-ROM ratings, notes, ## Deleting your account -Self-deletion isn't available in 5.0. Ask an admin to delete your account; they can do it from **Administration → Users → [your account] → Delete**. +Self-deletion isn't available in 5.0. Ask an admin to delete your account. They can do it from **Administration → Users → [your account] → Delete**. When deleted: @@ -135,6 +135,6 @@ See those pages for detail. ## Troubleshooting - **"Current password incorrect"**: caps lock, or the password was changed by an admin. Ask them. -- **Avatar upload silently fails**: file is too large; RomM accepts up to a few MB. Resize. +- **Avatar upload silently fails**: file is too large. RomM accepts up to a few MB. Resize. - **Can't create API token**: you've hit the 25-token cap. Revoke old ones. - **OIDC login came back as Viewer when I'm an Admin**: role claim mapping is misconfigured or missing. See [OIDC Troubleshooting → Role mapping](../troubleshooting/authentication.md#user-is-created-but-stays-viewer-even-though-they-should-be-admin). diff --git a/docs/using/collections.md b/docs/using/collections.md index e343e09b..42ce5ea2 100644 --- a/docs/using/collections.md +++ b/docs/using/collections.md @@ -5,7 +5,7 @@ description: Hand-curated ROM collections, create, edit, share, and delete. # Collections -A **collection** is a named group of ROMs you curate by hand. Add or remove games one at a time; the list is exactly what you pick. +A **collection** is a named group of ROMs you curate by hand. Add or remove games one at a time, and the list is exactly what you pick. RomM has three collection types. This page covers **standard** collections. For the other two: @@ -29,7 +29,7 @@ Two ways: 1. Run a search or apply filters in the library. 2. Click the **New Collection from results** icon next to the search bar. -3. Name it and save; RomM populates it with the current result set. +3. Name it and save, and RomM populates it with the current result set. This is the fastest way to bootstrap a collection from "all the PSX games tagged (USA)" or similar. @@ -39,7 +39,7 @@ Hover a game card → context menu (…) → **Add to Collection** → pick the Or from the game detail page → Context menu → **Collections** → toggle. -Bulk add is not available in 5.0 for standard collections; use [Smart Collections](smart-collections.md) if you want rule-based population. +Bulk add is not available in 5.0 for standard collections. Use [Smart Collections](smart-collections.md) if you want rule-based population. ## Removing ROMs @@ -68,7 +68,7 @@ For cross-instance sharing, generate a link via the collection drawer → **Copy Collection drawer → **Delete** → confirm. -Deletion only removes the collection itself. The ROMs inside it stay in the library; nothing touches the actual files. +Deletion only removes the collection itself. The ROMs inside it stay in the library, and nothing touches the actual files. ## Who can do what diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md index 09db4218..348fa2f6 100644 --- a/docs/using/console-mode.md +++ b/docs/using/console-mode.md @@ -7,7 +7,7 @@ description: TV + gamepad UI for RomM. Spatial navigation, SFX, focus sounds, no **Console Mode** is a second UI for RomM, living at `/console`, designed for TV screens and gamepad input. Same instance, same data, completely different UX. -New in 5.0. If you're on a desktop with a keyboard and mouse, you probably don't need it; the main [Library](library.md) view is your home. But if you're on: +New in 5.0. If you're on a desktop with a keyboard and mouse, you probably don't need it. The main [Library](library.md) view is your home. But if you're on: - A TV with RomM on a stick / Shield / Pi / mini-PC, - A handheld running muOS / Knulli / Batocera / Steam Deck, @@ -21,7 +21,7 @@ Three ways: 1. **Menu bar → Console icon** (looks like a small controller). 2. Type `/console` after your RomM URL: `https://romm.example.com/console`. -3. Set the default view to Console Mode in **Profile → User Interface → Default view**; this signs you in straight into `/console` every time. +3. Set the default view to Console Mode in **Profile → User Interface → Default view**. This signs you in straight into `/console` every time. Bookmarking `/console` on your TV browser is the most common pattern. @@ -120,11 +120,11 @@ Not yet in Console Mode (still use the main UI): ### Grid layout -**Settings → Console Mode → Grid size.** Pick small / medium / large card sizes. Large is best for 10-foot viewing; small is for close viewing on a phone or handheld. +**Settings → Console Mode → Grid size.** Pick small / medium / large card sizes. Large is best for 10-foot viewing, and small is for close viewing on a phone or handheld. ### Background art -**Settings → Console Mode → Backgrounds.** Use the focused game's screenshot as a faded background; makes Console Mode feel like a proper console UI. Off by default. +**Settings → Console Mode → Backgrounds.** Use the focused game's screenshot as a faded background. Makes Console Mode feel like a proper console UI. Off by default. ## Launching games @@ -136,7 +136,7 @@ If not, Console Mode shows a **Download** prompt with a QR code for mobile shari ### Pre-launch disc/save/state picker -For multi-disc games, Console Mode asks which disc to boot before launching. For games with existing saves/states, you can pick which to resume from. No dialogs in the middle of a session; everything's picked up front. +For multi-disc games, Console Mode asks which disc to boot before launching. For games with existing saves/states, you can pick which to resume from. No dialogs in the middle of a session, because everything's picked up front. ## Known limitations diff --git a/docs/using/downloads.md b/docs/using/downloads.md index 381c3717..1f509b44 100644 --- a/docs/using/downloads.md +++ b/docs/using/downloads.md @@ -19,7 +19,7 @@ For cases where you want the URL, not the file right now: sending it to another Context menu (…) on a game card → **Copy download link**, and the URL is on your clipboard. -Anyone with access to the link and the server can download. By default the link requires your session cookie or a bearer token; see [Third-party download auth](#third-party-download-auth) for the exception. +Anyone with access to the link and the server can download. By default the link requires your session cookie or a bearer token. See [Third-party download auth](#third-party-download-auth) for the exception. ## QR code @@ -67,17 +67,17 @@ curl -H "Authorization: Bearer rmm_..." \ ## Streaming to an emulator -Some emulators can take an HTTP URL directly; point them at the same URL the Copy Link button produces. With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` and a reverse proxy that restricts access, you can set up truly remote ROM loading from a handheld over Wi-Fi. +Some emulators can take an HTTP URL directly. Point them at the same URL the Copy Link button produces. With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` and a reverse proxy that restricts access, you can set up truly remote ROM loading from a handheld over Wi-Fi. ## Download history -Not tracked in 5.0. RomM doesn't log downloads for privacy reasons; use your reverse proxy's access log if you need to audit. +Not tracked in 5.0. RomM doesn't log downloads for privacy reasons. Use your reverse proxy's access log if you need to audit. ## Troubleshooting - **Download stalls at N%**: usually the reverse proxy buffering to disk. See [Reverse Proxy → Nginx Proxy Manager](../install/reverse-proxy.md#nginx-proxy-manager) for the `proxy_max_temp_file_size 0` fix. - **Multi-file zip download is corrupt**: disk may have filled up during streaming, or the nginx mod_zip build is broken. Check `docker logs romm | grep mod_zip`. -- **Bulk download ends early**: reverse proxy is enforcing a request timeout. Raise `proxy_read_timeout`; see [Kubernetes Troubleshooting](../troubleshooting/kubernetes.md#websockets-disconnect-immediately) for nginx-ingress annotation pattern. +- **Bulk download ends early**: reverse proxy is enforcing a request timeout. Raise `proxy_read_timeout`. See [Kubernetes Troubleshooting](../troubleshooting/kubernetes.md#websockets-disconnect-immediately) for nginx-ingress annotation pattern. ## API diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md index f58d0b2d..9508adc5 100644 --- a/docs/using/in-browser-play.md +++ b/docs/using/in-browser-play.md @@ -10,7 +10,7 @@ RomM ships with two in-browser emulators: - **EmulatorJS**: RetroArch cores compiled to WebAssembly. Covers NES, SNES, Genesis, N64, PSX, PSP, Saturn, and 30+ other platforms. - **Ruffle**: Flash / Shockwave emulator for browser games. -Hit the **Play** button on any supported game; the emulator loads full-screen in a new page. Same UI on desktop, mobile, and Console Mode. +Hit the **Play** button on any supported game, and the emulator loads full-screen in a new page. Same UI on desktop, mobile, and Console Mode. ## EmulatorJS @@ -72,7 +72,7 @@ If the platform doesn't support in-browser play, Play is replaced with Download. EmulatorJS maps keyboard and gamepad automatically. Defaults are core-specific, but approximate the original console layout. Rebind in-game via the **Menu** button during play. -Operator-level default overrides live in `config.yml`; see [Configuration File → `emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols). +Operator-level default overrides live in `config.yml`. See [Configuration File → `emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols). Keyboard + gamepad can be used simultaneously for multi-player: player 2 on the gamepad, player 1 on the keyboard, for example. @@ -88,7 +88,7 @@ Full details: [Saves & States](saves-and-states.md). ### Cheats -In-game Menu → **Cheats** → add manually or load a list. Saved cheats persist per-user per-ROM. RomM doesn't ship a cheat database; you bring your own codes. +In-game Menu → **Cheats** → add manually or load a list. Saved cheats persist per-user per-ROM. RomM doesn't ship a cheat database, so you bring your own codes. ### Screenshots @@ -128,7 +128,7 @@ See [Netplay](netplay.md). One-page deep dive on hosting/joining, ICE servers, a ### Controls -Flash games were typically designed for mouse + keyboard; Ruffle passes input through as-is. No controller mapping; gamepad-only users will struggle with most Flash titles. +Flash games were typically designed for mouse + keyboard, and Ruffle passes input through as-is. No controller mapping, so gamepad-only users will struggle with most Flash titles. ### Saves @@ -136,7 +136,7 @@ Ruffle writes Flash's local-storage to RomM's assets directory. Appears under th ### Supported games -Most 2D Flash games work. 3D Shockwave and some advanced ActionScript titles may have rendering glitches; see [Ruffle compatibility](https://ruffle.rs/#compatibility). +Most 2D Flash games work. 3D Shockwave and some advanced ActionScript titles may have rendering glitches. See [Ruffle compatibility](https://ruffle.rs/#compatibility). ### Metadata @@ -146,7 +146,7 @@ If you enable the [Flashpoint](../administration/metadata-providers.md#flashpoin - **Game won't boot**: check firmware is uploaded for platforms that need it. `docker logs romm | grep -i emulator` for server-side hints. - **Black screen, no audio**: core is incompatible with your browser. Try a different browser (Chrome / Firefox are most tested) or a different core via in-game Menu → **Core**. -- **Controls do nothing**: browser needs focus; click once on the emulator canvas. Some cores need a button press to enumerate gamepads. +- **Controls do nothing**: browser needs focus. Click once on the emulator canvas. Some cores need a button press to enumerate gamepads. - **Netplay "failed to start game"**: see [Netplay troubleshooting](../troubleshooting/netplay.md). - **DOS game fails to autorun**: try [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjs) in `config.yml`. diff --git a/docs/using/languages.md b/docs/using/languages.md index 8256d7a7..bb236ae2 100644 --- a/docs/using/languages.md +++ b/docs/using/languages.md @@ -29,7 +29,7 @@ RomM's UI is translated into 19 locales. Pick yours from **Profile → User Inte | `zh_CN` | Chinese (Simplified) | | `zh_TW` | Chinese (Traditional) | -Two more ship as work-in-progress with partial coverage; check the language dropdown to see the current list. +Two more ship as work-in-progress with partial coverage. Check the language dropdown to see the current list. ## What gets translated @@ -39,7 +39,7 @@ Two more ship as work-in-progress with partial coverage; check the language drop Not translated: -- **Game metadata**: titles, descriptions, genres come from metadata providers. English is usually the source language; IGDB has some localisation but coverage is uneven. +- **Game metadata**: titles, descriptions, genres come from metadata providers. English is usually the source language. IGDB has some localisation but coverage is uneven. - **Your own content**: collection names, notes, user-uploaded media. - **Docs site** (what you're reading): English only in 5.0. @@ -55,17 +55,17 @@ Spotted something missing? Help translate it, see below. 1. Fork [rommapp/romm](https://github.com/rommapp/romm). 2. Under `frontend/src/locales//`, open the JSON / YAML files. -3. Add the missing keys; they're all present in `en_US` for reference. +3. Add the missing keys. They're all present in `en_US` for reference. 4. Open a PR. ### Add a new locale 1. Create `frontend/src/locales//` by copying the structure from `en_US`. 2. Translate. -3. Register the locale in the `frontend/src/locales/index.ts` file (the filename may change; follow the pattern in the repo). +3. Register the locale in the `frontend/src/locales/index.ts` file (the filename may change, so follow the pattern in the repo). 4. Open a PR. -Partial translations are welcome; we'd rather have a 70%-translated locale up for people to improve than hold out for 100%. +Partial translations are welcome. We'd rather have a 70%-translated locale up for people to improve than hold out for 100%. See [Translations (i18n)](../developers/i18n.md) for the full contributor walkthrough. @@ -77,15 +77,15 @@ Your language choice is stored server-side on your user record, so it follows yo ### Right-to-left (RTL) -No RTL locales in 5.0. Arabic and Hebrew translations may land in a future release; the UI needs some layout work before it can present them cleanly. +No RTL locales in 5.0. Arabic and Hebrew translations may land in a future release, because the UI needs some layout work before it can present them cleanly. ### CJK typography -CJK locales (Japanese, Korean, Chinese simplified/traditional) use the browser's default system font stack. Some game titles with rare glyphs may render inconsistently across OSes; that's a browser font issue, not a RomM one. +CJK locales (Japanese, Korean, Chinese simplified/traditional) use the browser's default system font stack. Some game titles with rare glyphs may render inconsistently across OSes. That's a browser font issue, not a RomM one. ## Docs i18n -The docs site is currently English-only. Localising docs is a much bigger commitment than localising the app; if you'd like to help, open an issue on [rommapp/docs](https://github.com/rommapp/docs) and we can discuss scope. +The docs site is currently English-only. Localising docs is a much bigger commitment than localising the app. If you'd like to help, open an issue on [rommapp/docs](https://github.com/rommapp/docs) and we can discuss scope. ## Related diff --git a/docs/using/library.md b/docs/using/library.md index 5ee5c2f2..b07827ec 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -60,7 +60,7 @@ Double-click (or tap) the card to open the [game view](#game-view). ## Filters -Every gallery has a Filters button in the top right. Filter combinations stack; RomM shows only games matching all active filters. +Every gallery has a Filters button in the top right. Filter combinations stack, and RomM shows only games matching all active filters. ### Toggle filters @@ -108,7 +108,7 @@ Clicking a platform card takes you to the platform view: every game on that plat Two side buttons: -- **Platform drawer**: metadata for the platform itself: name, slug, category, generation, IGDB version, active providers, cover-art style setting. Admins see an **Upload** + **Scan** shortcut + a Danger Zone with "Delete Platform" (removes the DB entry; files on disk are not touched; a rescan re-creates the platform). +- **Platform drawer**: metadata for the platform itself: name, slug, category, generation, IGDB version, active providers, cover-art style setting. Admins see an **Upload** + **Scan** shortcut + a Danger Zone with "Delete Platform" (removes the DB entry, files on disk are not touched, and a rescan re-creates the platform). - **Firmware**: every firmware file registered against the platform, plus an upload button. See [Firmware Management](../administration/firmware-management.md). ## Collection view diff --git a/docs/using/mobile-and-tv.md b/docs/using/mobile-and-tv.md index 583ad620..d0c2f0fc 100644 --- a/docs/using/mobile-and-tv.md +++ b/docs/using/mobile-and-tv.md @@ -45,7 +45,7 @@ You've got a TV-attached Android box, a mini-PC, a Nvidia Shield, or a browser r 3. **Plug in a gamepad**: USB, Bluetooth, anything the browser sees. 4. **Launch the PWA.** You're in Console Mode, gamepad-ready. -Now it looks and feels like a console UI, running on a web page. Launching a game loads EmulatorJS full-screen; same gamepad passes through. +Now it looks and feels like a console UI, running on a web page. Launching a game loads EmulatorJS full-screen, and the same gamepad passes through. ### What about actual emulation? @@ -91,11 +91,11 @@ Some things are bandwidth-hungry, some aren't: | Activity | Typical bandwidth | Notes | | --- | --- | --- | -| Browsing the library | Low | Cover-art thumbnails; a few hundred KB per page load. | -| Playing in browser | Medium | ROM streams at boot; thereafter cached. PSP / Saturn ISOs can be hundreds of MB on first load. | +| Browsing the library | Low | Cover-art thumbnails, a few hundred KB per page load. | +| Playing in browser | Medium | ROM streams at boot, then cached. PSP / Saturn ISOs can be hundreds of MB on first load. | | Netplay | Medium–High | Video stream from host to players. ~500 kbps for SNES, more for higher-res cores. | | Bulk download | As-much-as-you-want | Rate-limited only by your reverse proxy and network. | -| Device sync (saves) | Low | Saves are small; sync is fast. | +| Device sync (saves) | Low | Saves are small, so sync is fast. | | Device sync (ROMs) | High | Pushing full ROM sets to a handheld initially is a lot. | On cellular? Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=false` (default: keep auth on) to avoid accidental discovery, and prefer native companion apps over in-browser play. @@ -106,7 +106,7 @@ If you host RomM on a home server and want to reach it from cellular: - Put it behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). - Use a VPN (Tailscale, WireGuard) instead of exposing to the internet. Handhelds with Tailscale setups "just work". -- For public access without a VPN, put Cloudflare Access or similar zero-trust auth in front of RomM. Disable `ALLOW_PUBLIC_REGISTRATION` on RomM; the edge auth handles gatekeeping. +- For public access without a VPN, put Cloudflare Access or similar zero-trust auth in front of RomM. Disable `ALLOW_PUBLIC_REGISTRATION` on RomM, because the edge auth handles gatekeeping. ## See also diff --git a/docs/using/netplay.md b/docs/using/netplay.md index c343917e..c68f4531 100644 --- a/docs/using/netplay.md +++ b/docs/using/netplay.md @@ -15,7 +15,7 @@ Best for 2-player co-op and turn-based games. Not ideal for twitchy fighting gam ## Prerequisites - EmulatorJS Netplay enabled in `config.yml` (operator-level). -- ICE servers configured (STUN + TURN); without them, Netplay only works when all players are on the same LAN. +- ICE servers configured (STUN + TURN). Without them, Netplay only works when all players are on the same LAN. - All players need access to your RomM instance. Netplay doesn't proxy the ROM to people without RomM accounts. See [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50) for the operator-side setup. @@ -28,7 +28,7 @@ See [Configuration File → `emulatorjs.netplay`](../reference/configuration-fil 4. **Create Room** → optional password. 5. Players who can reach your RomM see the room in the Netplay list. -You're Player 1. Up to three more players can join as Players 2–4 (core-dependent; some only support 2). +You're Player 1. Up to three more players can join as Players 2–4 (core-dependent, some only support 2). ## Joining a room @@ -41,14 +41,14 @@ Other players must be on **your RomM**. Netplay doesn't federate across instance ## Controls -Every player controls their own gamepad / keyboard locally; inputs are sent to the host, which runs the game and broadcasts video. Slight lag is expected over the internet (~50–150 ms typical); higher on transatlantic hops or with TURN in the path. +Every player controls their own gamepad / keyboard locally, and inputs are sent to the host, which runs the game and broadcasts video. Slight lag is expected over the internet (~50–150 ms typical), higher on transatlantic hops or with TURN in the path. ## ICE servers (the NAT stuff) WebRTC, the protocol Netplay uses, needs help to punch through consumer routers. That's what ICE (STUN + TURN) servers do: -- **STUN**: cheap; helps two peers find each other's public IPs. Works unless one of you is behind symmetric NAT. -- **TURN**: relays traffic when STUN can't. More resource-intensive; most free TURN services have quotas. +- **STUN**: cheap. Helps two peers find each other's public IPs. Works unless one of you is behind symmetric NAT. +- **TURN**: relays traffic when STUN can't. More resource-intensive, and most free TURN services have quotas. The operator wires ICE servers in `config.yml`: @@ -75,7 +75,7 @@ For production Netplay, operators should get a dedicated TURN account (free tier - **Not all cores support Netplay.** SNES9x, Mupen64Plus, Mednafen PSX, Genesis Plus GX: generally yes. Cores like PPSSPP or dosbox-pure: no. - **Frame-perfect fighting isn't realistic.** Netplay is for casual co-op, not tournament-level fighting games. Use something like [FightCade](https://www.fightcade.com/) for that. -- **All players need RomM access.** There's no "join a friend's game without an account"; guests need at least a Viewer account on your instance. +- **All players need RomM access.** There's no "join a friend's game without an account". Guests need at least a Viewer account on your instance. - **RTC over TURN uses real bandwidth.** Hosting a 4-player N64 session over TURN can saturate a modest uplink. Prefer STUN where possible. ## Known caveat: nightly CDN @@ -96,7 +96,7 @@ Netplay room data is short-lived, with no persistent record on RomM beyond the [ - **"Failed to start game"**: Netplay server-side config is broken. Operator: check `docker logs romm | grep -i netplay`. Usually a misconfigured ICE server URL. - **Other players can't see my room**: either they're not on your RomM (shouldn't happen if they have accounts) or the WebSocket connection is broken. See [WebSocket Troubleshooting](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). -- **Game plays fine locally but Netplay glitches**: network round-trip is too high. Test with Players on the same LAN first; add TURN and re-test from outside. -- **Audio desync**: known WebRTC issue; try a different browser (Chrome is most-tested), or restart the session. +- **Game plays fine locally but Netplay glitches**: network round-trip is too high. Test with Players on the same LAN first, then add TURN and re-test from outside. +- **Audio desync**: known WebRTC issue. Try a different browser (Chrome is most-tested), or restart the session. More in [Netplay Troubleshooting](../troubleshooting/netplay.md). diff --git a/docs/using/pwa.md b/docs/using/pwa.md index 8a9bc09d..7c5cb453 100644 --- a/docs/using/pwa.md +++ b/docs/using/pwa.md @@ -34,7 +34,7 @@ Similar: look for "Add to Home screen" in the share / menu. iOS Safari is slightly different and has no true PWA parity (some APIs are missing), but the basic install flow works: -1. Open your RomM URL in **Safari** (not Chrome; on iOS, Chrome uses Safari's engine but doesn't expose the install flow). +1. Open your RomM URL in **Safari** (not Chrome, because on iOS, Chrome uses Safari's engine but doesn't expose the install flow). 2. Tap the **Share** button (square with arrow). 3. **Add to Home Screen**. 4. Name it, tap **Add**. @@ -45,7 +45,7 @@ iOS limitations: - No push notifications. - Service worker caching is more aggressive / unpredictable than on Android. -- The browser UI is fully gone; only gesture-based navigation. +- The browser UI is fully gone, leaving only gesture-based navigation. ## Install on desktop @@ -81,18 +81,18 @@ Uninstalling just removes the shortcut and cached shell. Nothing server-side is ## Updating -The service worker checks for new versions on launch. If RomM updates, the next time you open the PWA you get the latest shell. Browser-managed; no manual action. +The service worker checks for new versions on launch. If RomM updates, the next time you open the PWA you get the latest shell. Browser-managed, no manual action. If a stale shell is causing issues (e.g. seeing old UI after an upgrade), force-refresh: - **Chrome/Edge mobile**: long-press the reload button → **Hard Reload**. - **iOS Safari**: Settings → Safari → Clear History and Website Data also removes the PWA cache. -- **Desktop PWAs**: the three-dot menu usually has a Reload option; hold Shift for a hard reload. +- **Desktop PWAs**: the three-dot menu usually has a Reload option. Hold Shift for a hard reload. ## Limitations - **Requires HTTPS**: PWAs don't install from plain-HTTP hosts. Make sure your RomM is behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). -- **Icons**: RomM ships 192×192 and 512×512 manifest icons. Some devices pick a mid-size fallback that looks slightly blurry; known limitation, we'll expand the icon set over time. +- **Icons**: RomM ships 192×192 and 512×512 manifest icons. Some devices pick a mid-size fallback that looks slightly blurry. Known limitation, we'll expand the icon set over time. - **No push notifications yet**: the PWA manifest doesn't register a notification handler in 5.0. Scan completion, task failures, etc. don't notify you. - **Offline mode is partial**: opening the installed PWA offline shows the shell, but you can't actually browse the library or play anything without the server reachable. @@ -107,8 +107,8 @@ PWA + [Console Mode](console-mode.md) is a powerful combo: - **No Install option in Chrome**: RomM isn't on HTTPS, or the manifest is missing/broken. Open devtools → Application → Manifest to diagnose. - **Icon shows as a generic globe**: manifest icons aren't loading. Check the image URLs in devtools → Application → Manifest. -- **App won't open offline**: expected; only the shell caches, not data. Network access is required for everything useful. +- **App won't open offline**: expected, because only the shell caches, not data. Network access is required for everything useful. ## Want a native app instead? -Community-made native apps are listed on [Ecosystem → Community Apps](../ecosystem/community-apps.md), including Argosy Launcher, RommBrowser, and more. PWA is the zero-install path; native apps give you platform integration (notifications, deeper OS hooks) where it matters. +Community-made native apps are listed on [Ecosystem → Community Apps](../ecosystem/community-apps.md), including Argosy Launcher, RommBrowser, and more. PWA is the zero-install path. Native apps give you platform integration (notifications, deeper OS hooks) where it matters. diff --git a/docs/using/retroachievements.md b/docs/using/retroachievements.md index 2f5988ca..5de360c6 100644 --- a/docs/using/retroachievements.md +++ b/docs/using/retroachievements.md @@ -11,7 +11,7 @@ When RomM is wired up to RetroAchievements, each user's progression (achievement ## Prerequisites -1. **RomM operator** has to enable the provider. See [Metadata Providers → RetroAchievements](../administration/metadata-providers.md#retroachievements). Check by looking for the achievement tab on any known-supported game; if it's absent, the provider isn't active. +1. **RomM operator** has to enable the provider. See [Metadata Providers → RetroAchievements](../administration/metadata-providers.md#retroachievements). Check by looking for the achievement tab on any known-supported game. If it's absent, the provider isn't active. 2. **You** need a [RetroAchievements account](https://retroachievements.org/) and your personal **API key**. ## Linking your account @@ -55,21 +55,21 @@ A small badge appears on games where RA achievements exist, regardless of whethe RetroAchievements distinguishes two play modes: - **Softcore**: save states allowed. Achievements still count. -- **Hardcore**: no save states. More points per achievement; flagged separately. +- **Hardcore**: no save states. More points per achievement, flagged separately. -RomM doesn't enforce hardcore; you toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement, but marked as softcore. +RomM doesn't enforce hardcore. You toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement, but marked as softcore. If you care about hardcore, use the in-game save feature instead of save states. See [Saves & States → RetroAchievements and states](saves-and-states.md#retroachievements-and-states). ## Supported platforms -Most 8/16-bit consoles and some later platforms: NES, SNES, Genesis, Game Boy family, PC Engine, Master System, Atari 2600/7800, N64, PlayStation, Saturn, Dreamcast, and more. Some platforms are hash-dependent; your ROM has to match the RA canonical dump. +Most 8/16-bit consoles and some later platforms: NES, SNES, Genesis, Game Boy family, PC Engine, Master System, Atari 2600/7800, N64, PlayStation, Saturn, Dreamcast, and more. Some platforms are hash-dependent, so your ROM has to match the RA canonical dump. Full up-to-date list + hash requirements: [RA consoles + games](https://retroachievements.org/systemList.php). ## Hash-based matching -Whether a ROM has RA support depends on the **hash** matching RA's database. Hash calculation runs as part of scans; if you've disabled hashing (`filesystem.skip_hash_calculation: true`), you lose RA matching. +Whether a ROM has RA support depends on the **hash** matching RA's database. Hash calculation runs as part of scans, so if you've disabled hashing (`filesystem.skip_hash_calculation: true`), you lose RA matching. Some tips: diff --git a/docs/using/rom-patcher.md b/docs/using/rom-patcher.md index 9d4f4f78..b2d6e596 100644 --- a/docs/using/rom-patcher.md +++ b/docs/using/rom-patcher.md @@ -23,7 +23,7 @@ New in 5.0. Powered by the `rom-patcher-js` library. | `.pmsr` | Paper Mario Star Rod. | | `.vcdiff` (`.xdelta`) | Generic binary diff. | -If your patch has an unusual extension, try renaming to one of the above; many are just different framings of the same underlying algorithm. +If your patch has an unusual extension, try renaming to one of the above. Many are just different framings of the same underlying algorithm. ## Getting there @@ -35,18 +35,18 @@ Menu bar → **Patcher** icon (a wrench). Admins and Editors can also reach it f 2. **Upload the patch**: same drag/drop zone below. 3. **Pick the target platform**: usually auto-detected from the ROM. 4. **Choose output**: - - **Download patched ROM**: saves a file locally; RomM keeps neither the original nor the result. + - **Download patched ROM**: saves a file locally. RomM keeps neither the original nor the result. - **Save to library**: adds the patched ROM as a new entry in your library, alongside the original. - Optional **filename**: customise the output name. Defaults to ` [patched].`. 5. **Apply**. -Everything runs client-side in the browser. The ROM never gets uploaded to the RomM server during patching (if you picked Download); nothing leaves your machine. +Everything runs client-side in the browser. The ROM never gets uploaded to the RomM server during patching (if you picked Download), so nothing leaves your machine. ## Save to library -When you pick **Save to library** instead of Download, RomM receives the final patched file and stores it as a new ROM. Goes in the same platform folder as the original, with the patched filename. A subsequent scan picks it up; a **Quick** scan is enough. +When you pick **Save to library** instead of Download, RomM receives the final patched file and stores it as a new ROM. Goes in the same platform folder as the original, with the patched filename. A subsequent scan picks it up, and a **Quick** scan is enough. -Metadata isn't inherited; the new ROM is **unmatched** until you run a scan. If the patch is listed on IGDB / ROMHacking.net as its own entry, matching may pick it up. Otherwise, match manually or tag with `(igdb-XXXX)`. See [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). +Metadata isn't inherited, so the new ROM is **unmatched** until you run a scan. If the patch is listed on IGDB / ROMHacking.net as its own entry, matching may pick it up. Otherwise, match manually or tag with `(igdb-XXXX)`. See [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). ## Permissions @@ -65,7 +65,7 @@ Many Japanese-only games have fan translations as IPS or BPS patches. Point the ### ROM hacks and rebalances -Kaizo Mario-type hacks, FF6 balance patches, etc. Same flow: the patched ROM gets its own entry; your original stays intact. +Kaizo Mario-type hacks, FF6 balance patches, etc. Same flow: the patched ROM gets its own entry, and your original stays intact. ### Regional fixes @@ -73,21 +73,21 @@ No-intro patches, region-free patches. Apply, download, and verify with the Hash ## Limits -- **Browser memory**: huge ROMs (PSX / Saturn / PSP ISOs, > 1 GB) can struggle in browsers with low memory limits. Safari is the most restrictive; Firefox / Chrome handle bigger files. +- **Browser memory**: huge ROMs (PSX / Saturn / PSP ISOs, > 1 GB) can struggle in browsers with low memory limits. Safari is the most restrictive, but Firefox / Chrome handle bigger files. - **Multi-file ROMs**: the Patcher operates on a single file. For multi-disc games, patch each ISO separately. - **Encrypted ROMs**: if the patch was authored against a decrypted ROM (e.g. DS `.nds` vs raw cartridge), your source has to match. - **Save format changes**: patches that alter save-data layout will invalidate existing save files. Back them up before applying. ## Verifying the result -The Patcher shows the pre-patch and post-patch hash (CRC32 + MD5) after apply; compare against whatever the patch author published. If hashes match: you've got the correct patched ROM. If not: wrong source ROM, wrong patch, or the patch archive was truncated. +The Patcher shows the pre-patch and post-patch hash (CRC32 + MD5) after apply. Compare against whatever the patch author published. If hashes match: you've got the correct patched ROM. If not: wrong source ROM, wrong patch, or the patch archive was truncated. ## Troubleshooting - **"Invalid patch format"**: wrong extension or corrupted file. Try re-downloading the patch. - **"Checksum mismatch"**: the patch was built against a different ROM revision than yours. Common with no-intro vs TOSEC dumps. Look up the patch author's expected source. - **Browser hangs on huge ROMs**: close other tabs, try Firefox, or patch outside the browser with `Floating IPS` or `BPS` CLI tools then upload the result. -- **"Save to library" produces a broken ROM**: usually the patcher succeeded but the file was corrupted in transit. Retry; if it persists, patch externally and upload. +- **"Save to library" produces a broken ROM**: usually the patcher succeeded but the file was corrupted in transit. Retry. If it persists, patch externally and upload. ## API diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md index 96762e01..47ff586a 100644 --- a/docs/using/saves-and-states.md +++ b/docs/using/saves-and-states.md @@ -8,7 +8,7 @@ description: Manage save files and save-states across in-browser play and synced Two different things, often confused: - **Save files**: the in-game save (`.srm`, `.sav`, `.save`, etc.). What the game writes when you use the in-game save feature. Works across emulator cores that share the format. -- **Save states**: a full memory snapshot of the emulator at a moment in time. Emulator-specific; a SNES9x state won't load in bsnes. +- **Save states**: a full memory snapshot of the emulator at a moment in time. Emulator-specific. A SNES9x state won't load in bsnes. Both are **per-user per-ROM** and stored under `/romm/assets///`. They follow you across browsers and devices. @@ -32,7 +32,7 @@ The save is available to [in-browser play](in-browser-play.md) on the next launc ## Uploading a state -Same flow under **Upload State**. Optional screenshot attachment; RomM autogenerates one when you create a state from in-browser play. Only matters when uploading from outside. +Same flow under **Upload State**. Optional screenshot attachment. RomM autogenerates one when you create a state from in-browser play. Only matters when uploading from outside. ## Creating during play @@ -41,14 +41,14 @@ In-game Menu in EmulatorJS: - **Save**: writes in-game save data back to RomM. Same as the native console's save-to-cartridge flow. - **Save State** / **Load State**: creates a full memory snapshot, or restores from one. RomM uploads the snapshot automatically. -There's no "forgot to upload" step; everything persists as soon as you do it in-emulator. +There's no "forgot to upload" step. Everything persists as soon as you do it in-emulator. ## Selecting on launch If a ROM has multiple saves or states, the pre-launch picker appears before the emulator loads: - **Save file** dropdown: which save to load on boot. -- **State** dropdown: optional; loads immediately after boot. +- **State** dropdown: optional, loads immediately after boot. [Console Mode](console-mode.md) surfaces the same picker on a larger gamepad-friendly dialog. @@ -74,7 +74,7 @@ Saves and states can sync to/from registered devices (Grout on muOS, DeckRommSyn - [SSH Sync](../administration/ssh-sync.md): operator-side config. - [Argosy Launcher](../ecosystem/argosy.md) / [Grout](../ecosystem/grout.md): per-app setup. -From the end-user side: once your device is paired and sync is running, saves made on the device appear in RomM within a couple of sync cycles (default: 15 minutes). Conflicts (same ROM saved on two devices between syncs) surface as two separate save entries; pick which to keep. +From the end-user side: once your device is paired and sync is running, saves made on the device appear in RomM within a couple of sync cycles (default: 15 minutes). Conflicts (same ROM saved on two devices between syncs) surface as two separate save entries, so pick which to keep. ## Format / core compatibility @@ -110,10 +110,10 @@ When you create a state via in-game Menu → Save State, EmulatorJS grabs a fram ## Troubleshooting -- **Save uploaded but the game doesn't see it**: wrong format for the core. Check the compatibility table above; re-upload or switch cores. +- **Save uploaded but the game doesn't see it**: wrong format for the core. Check the compatibility table above, then re-upload or switch cores. - **State loads a corrupted frame**: state was saved by a different build of the core. If RomM updated the emulator bundle, old states may not load cleanly. Re-create or start a fresh save. - **Save disappears after play**: the emulator didn't flush on quit. Use in-game **Save and Quit** instead of just closing the browser. -- **Can't upload, "file too large"**: reverse proxy limit; raise `client_max_body_size` / `proxy-body-size`. See [Reverse Proxy](../install/reverse-proxy.md). +- **Can't upload, "file too large"**: reverse proxy limit. Raise `client_max_body_size` / `proxy-body-size`. See [Reverse Proxy](../install/reverse-proxy.md). More in [Troubleshooting](../troubleshooting/index.md). diff --git a/docs/using/smart-collections.md b/docs/using/smart-collections.md index 71df4ddd..a05ea0d0 100644 --- a/docs/using/smart-collections.md +++ b/docs/using/smart-collections.md @@ -108,14 +108,14 @@ any of: - Title contains "zelda" ``` -(Combine `all of` + `any of` by nesting; the rule editor supports groups.) +(Combine `all of` + `any of` by nesting. The rule editor supports groups.) ## Public / private Same visibility model as standard collections: - **Private**: only you see it (your personal data fields matter). -- **Public**: everyone on the instance sees it. *Your* personal-data rules still apply; if your smart collection is "games I haven't finished", every user sees *your* unfinished games. +- **Public**: everyone on the instance sees it. *Your* personal-data rules still apply. If your smart collection is "games I haven't finished", every user sees *your* unfinished games. For shared rule sets across users, use metadata-only fields and keep the collection public. Rules that reference Personal data (status, rating, playtime, favourites) only make sense as private collections. @@ -138,7 +138,7 @@ Same as standard collections: removes the definition. ROMs stay in the library. ## Limitations - **No nested smart collections**: a smart collection can't reference another collection as a source. Compose rules directly. -- **Performance**: very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible; mentioned here for completeness. +- **Performance**: very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible, but mentioned here for completeness. - **Timezone**: "Release Year" uses UTC, not the user's timezone. Edge-case edge-of-year games might fall on the "wrong" side. ## API @@ -151,4 +151,4 @@ PUT /api/collections/smart/{id} # update DELETE /api/collections/smart/{id} # delete ``` -Rule schema is part of the POST body; see the [API Reference](../developers/api-reference.md) for the JSON structure. Requires `collections.read` / `collections.write`. +Rule schema is part of the POST body. See the [API Reference](../developers/api-reference.md) for the JSON structure. Requires `collections.read` / `collections.write`. diff --git a/docs/using/uploads.md b/docs/using/uploads.md index f567530c..4cc790f6 100644 --- a/docs/using/uploads.md +++ b/docs/using/uploads.md @@ -9,12 +9,12 @@ description: Upload ROMs, firmware, saves, states, and screenshots into RomM fro | Type | Permission | Where | Details | | --- | --- | --- | --- | -| **ROMs** | Admin, Editor | **Upload** in menu bar; **Upload ROM** on platform detail; ROM detail → Upload menu | Goes to the correct platform folder in `/romm/library`. | +| **ROMs** | Admin, Editor | **Upload** in menu bar, **Upload ROM** on platform detail, ROM detail → Upload menu | Goes to the correct platform folder in `/romm/library`. | | **Firmware / BIOS** | Admin, Editor | Platform detail → Firmware button → Upload | See [Firmware Management](../administration/firmware-management.md). | | **Saves** | Self (own games) | Game detail → Game Data tab → Upload Save | Per-ROM, per-user. | | **States** | Self (own games) | Game detail → Game Data tab → Upload State | Per-ROM, per-user. Optional screenshot attached. | | **Screenshots** | Self (own games) | Game detail → Screenshots tab → Upload | Per-ROM, per-user. | -| **Manuals** | Admin, Editor | Game detail → Manual tab → Upload (when empty) | PDF; becomes the Manual tab content. | +| **Manuals** | Admin, Editor | Game detail → Manual tab → Upload (when empty) | PDF, becomes the Manual tab content. | | **Cover art** | Admin, Editor | Game detail → Edit → Upload cover | Replaces the provider-fetched cover. | ## ROM upload @@ -26,7 +26,7 @@ description: Upload ROMs, firmware, saves, states, and screenshots into RomM fro 3. **+ ADD** → select files, or drag-and-drop onto the upload zone. 4. Click **Upload**. -Multiple files upload in parallel. Progress bars show per-file status; failures surface with an error per file rather than stopping the whole batch. +Multiple files upload in parallel. Progress bars show per-file status, and failures surface with an error per file rather than stopping the whole batch. ### Large uploads (chunked) @@ -62,7 +62,7 @@ For your own games. Useful for importing existing save files from another emulat 3. Optionally attach a screenshot (a thumbnail for the save). 4. Save. -The file's stored under `/romm/assets///saves/` and is available to your [in-browser play](in-browser-play.md) sessions. RomM doesn't rewrite the file contents; it's stored as-is. +The file's stored under `/romm/assets///saves/` and is available to your [in-browser play](in-browser-play.md) sessions. RomM doesn't rewrite the file contents. It's stored as-is. See [Saves & States](saves-and-states.md) for more. @@ -70,8 +70,8 @@ See [Saves & States](saves-and-states.md) for more. Emulator save-states. Same flow as saves but under **States**. -- State formats are emulator-specific; a SNES9x state won't load in bsnes. -- Screenshots can be attached; RomM generates one automatically from the emulator if you create a state from [in-browser play](in-browser-play.md). +- State formats are emulator-specific. A SNES9x state won't load in bsnes. +- Screenshots can be attached. RomM generates one automatically from the emulator if you create a state from [in-browser play](in-browser-play.md). ## Screenshot uploads @@ -93,7 +93,7 @@ Supported: PDF. Renders in the browser via the Manual tab. ## Cover art uploads -Admin / Editor. Game detail → Context menu (…) → **Edit** → **Upload cover** → PNG / JPG / WebP. Overrides provider-fetched cover. To revert: Edit → **Reset cover**; the next metadata refresh repopulates from providers. +Admin / Editor. Game detail → Context menu (…) → **Edit** → **Upload cover** → PNG / JPG / WebP. Overrides provider-fetched cover. To revert: Edit → **Reset cover**, and the next metadata refresh repopulates from providers. ## Permissions summary @@ -109,7 +109,7 @@ Full scope matrix in [Users & Roles](../administration/users-and-roles.md#scope- ## Troubleshooting - **`413 Request Entity Too Large`**: your reverse proxy or ingress is capping body size. See [Reverse Proxy](../install/reverse-proxy.md) for the `client_max_body_size 0` / `proxy-body-size: "0"` fix. -- **Upload progresses then fails at 99%**: the finalise step timed out. Usually reverse-proxy read timeout is too tight; raise it. +- **Upload progresses then fails at 99%**: the finalise step timed out. Usually reverse-proxy read timeout is too tight, so raise it. - **"File is not a valid ROM for this platform"**: RomM's extension check rejected the file. Either it's the wrong platform, or the extension is obscure. See [Folder Structure → Naming](../getting-started/folder-structure.md#naming-convention). - **Save upload silently doesn't appear in-emulator**: you uploaded to the wrong emulator core. Check [Saves & States → Emulator compatibility](saves-and-states.md) for the format → core matrix. diff --git a/docs/using/virtual-collections.md b/docs/using/virtual-collections.md index 44994240..755a8fdd 100644 --- a/docs/using/virtual-collections.md +++ b/docs/using/virtual-collections.md @@ -5,7 +5,7 @@ description: Auto-generated groupings by genre, developer, year, tag, with no ru # Virtual Collections -**Virtual collections** are auto-generated by RomM. You don't create or edit them; RomM looks at your library and groups ROMs by common metadata dimensions. +**Virtual collections** are auto-generated by RomM. You don't create or edit them. RomM looks at your library and groups ROMs by common metadata dimensions. New in 5.0. Compare with: @@ -29,7 +29,7 @@ Virtual collections are generated for several dimensions: | **Region** | "USA", "Japan", "Europe" | | **Tags** | Any `[]`/`()` tag from filenames, see [Folder Structure → Naming convention](../getting-started/folder-structure.md#naming-convention) | -Collections with too few ROMs are suppressed; you won't see a "Developer: Some Indie Studio" with one entry. +Collections with too few ROMs are suppressed, so you won't see a "Developer: Some Indie Studio" with one entry. ## Turning them on and off @@ -37,7 +37,7 @@ All dimensions on by default. To turn a dimension off: **Profile → User Interface → Virtual Collections → toggle a dimension.** -Disabling a dimension hides all its collections from your drawer and dashboard. The underlying data is still there; you can re-enable any time. +Disabling a dimension hides all its collections from your drawer and dashboard. The underlying data is still there, so you can re-enable any time. ## Using them @@ -54,7 +54,7 @@ Open one like any collection: grid/list view, filters, play, download, etc. Virtual collections are read-only. You can't: - Add a ROM by hand (use a [standard collection](collections.md)). -- Remove a ROM (its metadata determines membership; fix the metadata if it's wrong). +- Remove a ROM (its metadata determines membership, so fix the metadata if it's wrong). - Make one "public" vs "private" (they're per-user, always visible to you). - Rename them (they take their name from the metadata dimension). - Set a custom cover image. @@ -76,8 +76,8 @@ Almost always metadata. A game missing from "Virtual Collection: RPG" probably d Genre coverage varies by metadata provider: - **IGDB**: rich genre data. -- **ScreenScraper**: ok genre data; some provider-specific labels. -- **Hasheous / PlayMatch** (hash-only): no genre data; they proxy IGDB. +- **ScreenScraper**: ok genre data, with some provider-specific labels. +- **Hasheous / PlayMatch** (hash-only): no genre data, because they proxy IGDB. If your library is heavily ScreenScraper-matched and you want rich genres, add IGDB and run an **Unmatched** or **Update Metadata** scan. See [Metadata Providers](../administration/metadata-providers.md). @@ -91,6 +91,6 @@ GET /api/collections/virtual?type=genre # filter by dimension GET /api/collections/virtual/{id} # get one ``` -There's no POST / PUT / DELETE; RomM owns the lifecycle. +There's no POST / PUT / DELETE. RomM owns the lifecycle. Requires `collections.read` scope. See the [API Reference](../developers/api-reference.md). From ec96037a57c6979419ac469e138d22f7f961014c Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 18:27:09 -0400 Subject: [PATCH 030/121] tweaks --- docs/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 3e8e52c7..a0cecee0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,7 +17,7 @@ Welcome to the **RomM Project**, the premier self-hosted, open source ROM manage Website · Demo · Discord -RomM (ROM Manager) lets you scan, enrich, organise, and play your game collection from a clean web UI, with metadata from IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, LaunchBox, and more; in-browser play via EmulatorJS and Ruffle; companion apps for Android, handhelds, and desktop; and a first-class multi-user experience with OIDC SSO. +RomM (ROM Manager) lets you scan, enrich, organise, and play your game collection from a clean web UI, with metadata from IGDB, ScreenScraper, MobyGames, RetroAchievements, Hasheous, LaunchBox, and more. In-browser play via EmulatorJS and Ruffle, companion apps for Android, handhelds, and desktop, and a first-class multi-user experience with OIDC SSO. ## Where do you want to go? @@ -27,7 +27,7 @@ RomM (ROM Manager) lets you scan, enrich, organise, and play your game collectio --- - Fifteen-minute Docker Compose walkthrough to a working RomM instance. + 15 minute Docker Compose walkthrough to a working RomM instance. [Quick Start →](getting-started/quick-start.md) From 9b13ad50f08acf83c30fdb6a69fd7a59cf468828 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 18:41:05 -0400 Subject: [PATCH 031/121] finish folder struct page --- docs/getting-started/folder-structure.md | 26 +++++++----------------- docs/getting-started/quick-start.md | 25 +++++++++++------------ docs/reference/configuration-file.md | 2 +- mkdocs.yml | 1 - 4 files changed, 20 insertions(+), 34 deletions(-) diff --git a/docs/getting-started/folder-structure.md b/docs/getting-started/folder-structure.md index b70115ea..cfdc4769 100644 --- a/docs/getting-started/folder-structure.md +++ b/docs/getting-started/folder-structure.md @@ -7,7 +7,7 @@ description: How to organise your ROM library on disk so RomM can scan and match # Folder Structure -RomM expects your library to be organised in one of two layouts. It tries **Structure A** first, and falls back to **Structure B** if A isn't found. This auto-detection is per-library, so you don't pick one up front. Just arrange files the way you prefer, and RomM figures it out. +RomM expects your library to be organised in one of two layouts. It tries **Structure A** first, and falls back to **Structure B** if A isn't found. This auto-detection is per-library (not per-platform), so you don't pick one up front. Just arrange files the way you prefer, and RomM figures it out. ## The two layouts @@ -38,23 +38,13 @@ The path you mount into the RomM container as `/romm/library` depends on which s See the [reference Docker Compose](../install/docker-compose.md) for where `/romm/library` lives. -## Multi-file games - -Some games come as **folders** instead of single files: multi-disc, DLC, manuals, patches. RomM understands this layout and recognises these sub-folder names, surfacing them as tags in the UI: - -- `dlc` -- `hack` -- `manual` -- `mod` -- `patch` -- `update` -- `demo` -- `translation` -- `prototype` - !!! tip "Platform folder names" The platform folder name has to match a known slug (`gbc`, `gba`, `ps`, `snes`, etc.). Full list in [Supported Platforms](../platforms/supported-platforms.md). If your existing folder names don't match (say, `super_nintendo/` instead of `snes/`), override the mapping via `system.platforms` in [`config.yml`](../reference/configuration-file.md). +## Multi-file games + +Some games come as **folders** instead of single files, which could include multiple disc, DLCs, manuals, or patches. RomM understands this layout and recognises these sub-folder names, surfacing them as tags in the UI: `dlc`, `hack`, `manual`, `mod`, `patch`, `update`, `demo`, `translation`, `prototype`. + ## Visual reference @@ -174,6 +164,8 @@ Filenames are parsed for region, language, revision, and arbitrary tags. Both `[ - **Revision**: prefix with `rev` / `rev-`. Example: `rev v1`, `rev-1`. - **Arbitrary tags**: anything else in brackets is imported verbatim. Example: `tetris [1.0001](HACK)[!].gba`. +RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry, covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). + Tags are searchable in the search bar: typing `(USA)` returns every game tagged USA.
@@ -244,7 +236,3 @@ Tags are searchable in the search bar: typing `(USA)` returns every game tagged
- -## Filename metadata tags - -RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry, covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index 792eab01..db6c5fd3 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -5,7 +5,7 @@ description: Get a RomM 5.0 instance running in about fifteen minutes using Dock # Quick Start -This guide gets a RomM instance up and running with the default stack (MariaDB + Redis + RomM) using Docker Compose. If you're on Unraid, Synology, TrueNAS, or Kubernetes, start there instead: the [Install & Deploy](../install/index.md) section has platform-specific guides. +This guide gets a RomM instance up and running with the default stack (MariaDB + Valkey) using Docker Compose. If you're on Unraid, Synology, TrueNAS, or Kubernetes, start there instead: the [Install & Deploy](../install/index.md) section has platform-specific guides. ## Before you start @@ -13,10 +13,10 @@ You'll need: - [Docker](https://docs.docker.com/get-docker/) and Docker Compose installed on the host. - Your ROM files organised in the expected [folder structure](folder-structure.md). -- API credentials for at least one [metadata provider](../administration/metadata-providers.md). IGDB + ScreenScraper is the recommended pairing. RomM will run without any provider configured, but matching quality will suffer. +- API credentials for at least one [metadata provider](../administration/metadata-providers.md). Hasheous + IGDB + SteamGridDB + Retroachievements is the recommended pairing. RomM will run without any provider configured, but matching quality will suffer. !!! warning "Metadata providers are recommended" - RomM works without a metadata API for basic use, but setup problems and companion-app integrations (e.g. Playnite) can fail without them. Setting up **IGDB** API keys before your first scan is strongly recommended. + RomM works without a metadata API for basic use, but setup problems and companion-app integrations (e.g. Playnite) can fail without them. Setting up **IGDB**, **SteamGridDB**, and **Retroachievements** API keys before your first scan is strongly recommended. ## 1. Write your `docker-compose.yml` @@ -31,14 +31,14 @@ You'll want to edit the following values before launching: | Where | Variable | What to put | | --- | --- | --- | -| `romm-db` service | `MARIADB_ROOT_PASSWORD` | A long random password. Generate one. Don't reuse. | -| `romm-db` service | `MARIADB_PASSWORD` | A separate long random password for the `romm-user`. | -| `romm` service | `DB_PASSWD` | Must match `MARIADB_PASSWORD` above. | -| `romm` service | `ROMM_AUTH_SECRET_KEY` | Generate with `openssl rand -hex 32`. Keep it secret. | -| `romm` service | Metadata provider creds | Fill in only the providers you've registered with. See [Metadata Providers](../administration/metadata-providers.md). | -| `romm` service | `/path/to/library` volume | Absolute path to the directory containing your `roms/` folder. | -| `romm` service | `/path/to/assets` volume | Absolute path where RomM will store saves, states, uploaded screenshots. | -| `romm` service | `/path/to/config` volume | Absolute path to a directory that will hold `config.yml`. | +| `romm-db` | `MARIADB_ROOT_PASSWORD` | A long, randomly generated password. | +| `romm-db` | `MARIADB_PASSWORD` | A separate long, randomly generated password. | +| `romm` | `DB_PASSWD` | Must match `MARIADB_PASSWORD` above. | +| `romm` | `ROMM_AUTH_SECRET_KEY` | Generate with `openssl rand -hex 32` and keep it secret. | +| `romm` | Metadata provider creds | Fill in only the providers you've registered with (see [Metadata Providers](../administration/metadata-providers.md)). | +| `romm` | `/path/to/library` volume | Host path to the directory containing your `roms/` folder. | +| `romm` | `/path/to/assets` volume | Host path where RomM will store saves, states, uploaded screenshots. | +| `romm` | `/path/to/config` volume | Host path to a directory that will hold `config.yml`. | Generate the auth secret now so you don't forget: @@ -90,9 +90,8 @@ The fastest way to populate RomM is to let it scan your mounted library: 3. Start the scan. You can open any matched game while the scan continues to see the metadata RomM pulled. 4. When the scan finishes, click the RomM logo to return home. You'll see your platforms and the most recently added games. -That's it, you're running RomM. From here: +That's it, you're up and running! From here: -- Customise the on-disk layout: [Folder Structure](folder-structure.md) - Add more users and lock down access: [Users & Roles](../administration/users-and-roles.md) - Put RomM behind a reverse proxy with HTTPS: [Reverse Proxy](../install/reverse-proxy.md) - Learn what all those settings mean: [Core Concepts](concepts.md) diff --git a/docs/reference/configuration-file.md b/docs/reference/configuration-file.md index be7696bb..ee85e92d 100644 --- a/docs/reference/configuration-file.md +++ b/docs/reference/configuration-file.md @@ -1,6 +1,6 @@ --- title: Configuration File -description: Full schema reference for config.yml: exclusions, system bindings, filesystem, scan priorities, EmulatorJS. +description: Full schema reference for config.yml --- # Configuration File diff --git a/mkdocs.yml b/mkdocs.yml index c55025b1..56b55585 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -69,7 +69,6 @@ plugins: redirect_maps: # Getting Started → new IA Getting-Started/Quick-Start-Guide.md: getting-started/quick-start.md - Getting-Started/Folder-Structure.md: getting-started/folder-structure.md Getting-Started/Configuration-File.md: reference/configuration-file.md Getting-Started/Environment-Variables.md: reference/environment-variables.md Getting-Started/Metadata-Providers.md: administration/metadata-providers.md From 24aede16e3184be2d74b50d15a084ad889c84118 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Apr 2026 22:49:02 +0000 Subject: [PATCH 032/121] docs: patch gaps found cross-referencing release history MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audited every major/minor release from v1.5 through v4.8 against the docs. Four real gaps found: - using/library.md: pipe-delimited multi-keyword search (`zelda|mario|sonic`) from 4.7 — added sub-section in Search - using/library.md: notes tab now documented separately from Personal — Markdown formatting, public/private sharing, multiple notes per game (3.2 + 4.5 features) - using/library.md: playtime explicitly called out on Personal tab (auto-accumulated from in-browser play + companion apps) - using/downloads.md: Nintendo 3DS direct-install via QR (3.7 feature) added as a sub-section under QR Code - using/account-and-profile.md: Accessibility section added — screen-reader support, keyboard-only nav, reduced-motion, 19 locales, plus known Console Mode gap (3.10 a11y feature) Other release-notes features cross-checked and confirmed already covered: auto-generated / virtual collections (3.8), Smart Collections (4.1), missing-games page (4.1), invite links (3.10), client API tokens + pairing (4.8), chunked uploads (4.8), OIDC autologin (4.7), ES-DE gamelist (4.4), multi-threaded scanning (4.4), Kiosk mode (3.8), Netplay (4.5), ROM Patcher (4.6), Console Mode (4.2), OpenTelemetry (4.1), scheduled task system revamp (4.0), hash-based matching + Hasheous/Playmatch (4.0), LaunchBox integration (4.0 + 4.8), SteamGridDB (3.3), Ruffle (3.5), nginx mod_zip (3.5), Personal tab (3.6), age ratings (3.6), i18n (3.7), OIDC (3.7), slim image (3.7), ScreenScraper (3.8), RetroAchievements (3.10), BIOS/firmware (3.2), markdown notes (3.2), Redis built-in + AGPL (3.0), saves+states (3.0), EmulatorJS (3.0). Builds clean with --strict. --- docs/using/account-and-profile.md | 18 ++++++++++++++++++ docs/using/downloads.md | 10 ++++++++++ docs/using/library.md | 13 ++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index b3ac03b4..d440adef 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -86,6 +86,24 @@ Available throughout the app: | `j` / `k` | Next / previous item in a list view. | | `Enter` | Activate focused item. | +## Accessibility + +RomM's main UI supports screen readers (VoiceOver, NVDA, JAWS, Orca). Every interactive element has proper ARIA labels, focus states are visible, and semantic heading structure is consistent. + +Other accessibility features: + +- **Keyboard-only navigation**: every user-facing action reachable without a mouse. Tab through interactive elements, and the shortcuts above get you around quickly. +- **Dark / light / auto theme**: Profile → User Interface → Theme. Auto follows OS preference. +- **Reduced motion**: RomM respects `prefers-reduced-motion` from your OS, so animations shorten or disable automatically. +- **19 locales**: see [Languages](languages.md). + +Gaps you should know about: + +- **[Console Mode](console-mode.md)** (the `/console` SPA) isn't currently screen-reader-friendly the way the main UI is. It's built around gamepad / spatial navigation. Stick with the main UI if you use assistive tech. +- **In-browser play**: EmulatorJS is a third-party component and its accessibility is outside RomM's control. + +Known issue? Please file on [rommapp/romm](https://github.com/rommapp/romm/issues). Accessibility fixes get prioritised. + ## Personal data Per-game data you set (ratings, notes, status, playtime) is all stored per-user. Admins can't see your per-game ratings unless you share them via a public collection. diff --git a/docs/using/downloads.md b/docs/using/downloads.md index 1f509b44..8f4d88ac 100644 --- a/docs/using/downloads.md +++ b/docs/using/downloads.md @@ -29,6 +29,16 @@ Context menu (…) → **Show QR Code**, then point the other device's camera at The QR decodes to the same URL as Copy Link. Same auth rules apply. +### Nintendo 3DS direct install + +The 3DS built-in QR scanner can install compatible `.cia` files directly from a URL. For supported Nintendo 3DS files, RomM's QR code becomes a one-tap install path: + +1. Open the ROM on RomM → context menu → **Show QR Code**. +2. On the 3DS, launch FBI (or another CIA installer that accepts QR input). +3. Select **Install from URL** → **Scan QR** → point at your screen. + +Same prerequisites as any Copy Link / QR download: the 3DS needs network access to your RomM instance, and the file has to be accessible (either authenticated with basic-auth support on the 3DS side, or `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` behind upstream auth). + ## Multi-file and bulk downloads (nginx `mod_zip`) RomM's nginx is built with `mod_zip`, which streams a zip archive over HTTP without ever materialising the file on disk. Two places this matters: diff --git a/docs/using/library.md b/docs/using/library.md index b07827ec..08944ac6 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -93,6 +93,16 @@ Click the search icon in the menu bar. Real-time search against: Search is platform-aware: type `zelda` and you'll see every matched Zelda across every platform you have. +### Multi-keyword search + +Separate terms with `|` to match any of them: + +```text +zelda|mario|sonic +``` + +Returns every game whose metadata matches `zelda` **or** `mario` **or** `sonic`. Useful for building a collection from a quick ad-hoc pattern: combine with the "New collection from results" button below. + ![search bar](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/SearchBar.png) Two icons next to the search bar: @@ -135,7 +145,8 @@ Which tabs appear depends on your metadata providers: - **Details**: title, description, release date, genres, developer/publisher, regions, rating, matched providers. Filterable metadata surfaces here. - **Game Data**: save files, save states, screenshots. Per-user. Upload, download, and delete. See [Saves & States](saves-and-states.md). -- **Personal**: your data on this game: status (Never Played / Backlogged / Playing / Complete / Hidden), rating, difficulty, percent complete, notes. Stored per-user. +- **Personal**: your data on this game: status (Never Played / Backlogged / Playing / Complete / Hidden), rating, difficulty, percent complete, and **playtime** (accumulated as you play via the [in-browser player](in-browser-play.md) or companion apps that post play sessions). Stored per-user. +- **Notes**: Markdown-formatted notes on the game. Per-user. Set a note to **public** to share with other users on the instance. You can have multiple notes per game (e.g. "walkthrough tips", "save states reference", "secret moves"). Edited via a Markdown editor with preview pane. - **Manual**: PDF viewer if you have a manual for this game. - **Time to Beat**: [HowLongToBeat](../administration/metadata-providers.md#howlongtobeat) data if enabled. - **Screenshots**: provider-fetched + user-uploaded screenshots. From 39936e254dd34529cdc9e75b33f550f745adf1d0 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 22:04:25 -0400 Subject: [PATCH 033/121] tons of cleanup --- .github/workflows/deploy.yml | 5 +- .github/workflows/pr-checks.yml | 8 +- docs/platforms/supported-platforms.md | 4 +- docs/reference/environment-variables.md | 2 +- docs/reference/scheduled-tasks.md | 2 +- .../resources/metadata_providers/libretro.png | Bin 0 -> 2354 bytes docs/resources/snippets/env-vars.md | 2 +- docs/resources/snippets/scheduled-tasks.md | 2 +- .../resources/snippets/supported-platforms.md | 466 +++++++++++++++++- .../__pycache__/_sources.cpython-311.pyc | Bin 2714 -> 0 bytes docs/scripts/gen_platforms.py | 37 -- pyproject.toml | 3 +- {docs/scripts => scripts}/_sources.py | 13 +- {docs/scripts => scripts}/check_redirects.py | 7 +- {docs/scripts => scripts}/gen_env_vars.py | 6 +- scripts/gen_platforms.py | 73 +++ .../gen_scheduled_tasks.py | 6 +- {docs/scripts => scripts}/sources.toml | 0 uv.lock | 11 + 19 files changed, 571 insertions(+), 76 deletions(-) create mode 100644 docs/resources/metadata_providers/libretro.png delete mode 100644 docs/scripts/__pycache__/_sources.cpython-311.pyc delete mode 100644 docs/scripts/gen_platforms.py rename {docs/scripts => scripts}/_sources.py (79%) rename {docs/scripts => scripts}/check_redirects.py (90%) rename {docs/scripts => scripts}/gen_env_vars.py (95%) create mode 100644 scripts/gen_platforms.py rename {docs/scripts => scripts}/gen_scheduled_tasks.py (95%) rename {docs/scripts => scripts}/sources.toml (100%) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 72091f6b..8c97a5ab 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -44,9 +44,8 @@ jobs: - name: Refresh generated snippets run: | - uv run python docs/scripts/gen_env_vars.py - uv run python docs/scripts/gen_platforms.py - uv run python docs/scripts/gen_scheduled_tasks.py + uv run python -m scripts.gen_env_vars + uv run python -m scripts.gen_scheduled_tasks - name: Set Git user run: | diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 81713660..9f80e100 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -34,17 +34,11 @@ jobs: - name: Install dependencies run: uv sync --all-extras --dev - - name: Refresh generated snippets - run: | - uv run python docs/scripts/gen_env_vars.py - uv run python docs/scripts/gen_platforms.py - uv run python docs/scripts/gen_scheduled_tasks.py - - name: Build docs (strict) run: uv run mkdocs build --strict - name: Verify redirect targets exist - run: uv run python docs/scripts/check_redirects.py + run: uv run python -m scripts.check_redirects link-check: runs-on: ubuntu-latest diff --git a/docs/platforms/supported-platforms.md b/docs/platforms/supported-platforms.md index 369f9626..b00d7c78 100644 --- a/docs/platforms/supported-platforms.md +++ b/docs/platforms/supported-platforms.md @@ -36,10 +36,10 @@ Open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) if you b ## Where the table comes from -Generated by `docs/scripts/gen_platforms.py` against the upstream platform registry at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). Regenerate locally with: +Generated by `scripts/gen_platforms.py` against the upstream platform registry at the SHA pinned in [`scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/scripts/sources.toml). Regenerate locally with: ```sh -uv run python docs/scripts/gen_platforms.py +uv run python -m scripts.gen_platforms ``` ## See also diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 5bc991da..52b3c43e 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -7,7 +7,7 @@ description: Every environment variable RomM reads, grouped by what it controls. Everything RomM does that's not in [`config.yml`](configuration-file.md) is driven by env vars. Set them on the `romm` service in your compose file, as Unraid / Synology / TrueNAS container env vars, or on your Kubernetes deployment. -This page is the **authoritative lookup**: every var RomM reads. The table is generated directly from [`rommapp/romm`'s `env.template`][src] at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). When RomM adds an env var, the next docs bump re-runs the generator and this page updates. +This page is the **authoritative lookup**: every var RomM reads. The table is generated directly from [`rommapp/romm`'s `env.template`][src] at the SHA pinned in [`scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/scripts/sources.toml). When RomM adds an env var, the next docs bump re-runs the generator and this page updates. [src]: https://github.com/rommapp/romm/blob/master/env.template diff --git a/docs/reference/scheduled-tasks.md b/docs/reference/scheduled-tasks.md index f744b0ff..345fbdaa 100644 --- a/docs/reference/scheduled-tasks.md +++ b/docs/reference/scheduled-tasks.md @@ -43,7 +43,7 @@ Two approaches: ## Where the table comes from -Generated by `docs/scripts/gen_scheduled_tasks.py` against the task registry at the SHA pinned in [`docs/scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/docs/scripts/sources.toml). When RomM adds or renames a task, the next docs release regenerates the table. +Generated by `scripts/gen_scheduled_tasks.py` against the task registry at the SHA pinned in [`scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/scripts/sources.toml). When RomM adds or renames a task, the next docs release regenerates the table. ## See also diff --git a/docs/resources/metadata_providers/libretro.png b/docs/resources/metadata_providers/libretro.png new file mode 100644 index 0000000000000000000000000000000000000000..c674caf385f906f1beca8237a48bbe756370670b GIT binary patch literal 2354 zcmcImi#O8`6en+@qNoX_JQk8#p5Zr)OvEg&!Yc9_lChdlTMh=k=@o7V;Xy zPiaEhM&2>~kkL?ly_28*h~GWubMEKfbMF0|d(OS*+!R+A2YFeLtcZw+yyJO0gouc! zUr15fSMDM>{C;_VC>3 zXxfCSa?;h*S~&^iRm0|yZrZ{i@4{|z$S| z6~E2Da?hAWUi?Atk8`cveA9h38XjAKaY;m_%i;DA*zOq~5Luw`ZiN!gU*fuh5cR6p z(O-dMMqZM68Qgz!Er^)MQ3IP%)h{+yY~BPjn7MW%wZ$g_lu_| z{QlXcJKtClT0JweFX~9iOtI7UKqc>NnLNno3C+n!_acco`T1#BMZC-21HtvgQyS;9 z{A1)y9Jqn$Ln~#yenW%*o+N%S_77;XKPKR?wCkq;2sI1HeHxyVzueq9^2Ox9*b&*R3P3m=PE=D}(G52wKEYbZ;4vE5}2b`B8$j-RS8 zG52P6Bcsy$>o*J=Fh*A_2h-9@oO`Ndc6){)7T-Ddv}!;4@}fj@u6Hu5$gU1qOwoxm zf&(vTy<(C2S{sm;?jZbVcEmi{9F8$`2kc?yU24GzQptWohEHSVY6lp%4(f9=yrFD- zJ8U%QY%Z)Nri!;^dnHk{4ZFIB0$l4SyIIs&=LVm9pc&5(^J%e(=_6VqdNWm4tSR)* zz7&DO$@^hGi{Cvhp_lz@2SHXxgWYhgb8xg8#~9VO>$eWRgr8MZN-&}WmvFcNgN+mAjYPDfxl7wwPLDsQQpHo!0cWvmE5G_pUfo8yeWof5Pf)Ci?wRdspJl1=a4Cf|~32&#TZY=X%jcUc7}sb23ktxO;k`)4#9PKa+^;yUjzcL%BlW)Wo%TTc0N$kKz!JbA`<#Yu-!^&#CaA!bGd`o*Js6Z;`2MQRAsp#Zf8zyj}Tvf4qM7-KL8JUqrok z_HU4-8t3MfLh1|x)3p}z+uYTq;DoR}P$f(q+jBtu>uZb2oQWa5g=4{iZ*>ZrG`kDm zTV9XLhBSF;gqd)-e3dKL$Ah|RO(Mg)!QL#F(97-cu)0rE; ze@4$8qXElFn-AfgeL#ux!9~rX>SzBv?{7++KNh@mTu{~0cd@Autp>7F4!D6ttqhFx1}qgt zY-ZR~%SL(`jJOlmWS2Xe^jm`54w3%46Nn!p7j>s&rRpW9O6NC1WQ}!LSxf1SWV=l^0LkgZ|lluy1TtsXQ4(=e~|gK&w*5B}S03VmC@rs8`@KyLmIkk8uU2$cD6?4q<^_)qg+ z2Gu{zV_AwPBHI!W=&OOlU9`&kyS503Glqe2@VP6J1p6eqOsc_wTIf*y(5AY?%kR>- zxyZ5P`6PYA%e2xj?!2#4V;Rj!=)Wz6wBX0R+vBzo6;6TgH;M~eXFXG$o9PGkf9*3R iR&O-?&!2*bYX)EREz3cf_icp#TgwsVV#lyS;r|20Z%#D; literal 0 HcmV?d00001 diff --git a/docs/resources/snippets/env-vars.md b/docs/resources/snippets/env-vars.md index 92e75141..0568f385 100644 --- a/docs/resources/snippets/env-vars.md +++ b/docs/resources/snippets/env-vars.md @@ -1,4 +1,4 @@ - + ### Core Application diff --git a/docs/resources/snippets/scheduled-tasks.md b/docs/resources/snippets/scheduled-tasks.md index 22a62afd..1c72055a 100644 --- a/docs/resources/snippets/scheduled-tasks.md +++ b/docs/resources/snippets/scheduled-tasks.md @@ -1,4 +1,4 @@ - + | Task | Type | Default schedule | Env var | Purpose | | --- | --- | --- | --- | --- | diff --git a/docs/resources/snippets/supported-platforms.md b/docs/resources/snippets/supported-platforms.md index 9c841df4..16a2894f 100644 --- a/docs/resources/snippets/supported-platforms.md +++ b/docs/resources/snippets/supported-platforms.md @@ -1,6 +1,462 @@ - - + -_Supported-platforms table is generated at build time. Run -`uv run python docs/scripts/gen_platforms.py` to refresh._ +|Platform Name|Folder Name|Metadata Providers| +|---|---|---| +| 1292 Advanced Programmable Video System | `1292-advanced-programmable-video-system` | igdb logo mobygames logo | +| 3DO Interactive Multiplayer | `3do` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| 8-Bit Productions Commander X16 | `commander-x16` | hasheous logo | +| Aamber Pegasus | `pegasus` | screenscraper logo launchbox logo | +| ABC 80 | `abc-80` | mobygames logo | +| Acorn Archimedes | `acorn-archimedes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Acorn Electron | `acorn-electron` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Action Max | `action-max` | screenscraper logo launchbox logo hasheous logo | +| Advanced Pico Beena | `advanced-pico-beena` | igdb logo | +| Adventure Vision | `adventure-vision` | screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | +| AirConsole | `airconsole` | igdb logo mobygames logo | +| Alice 32/90 | `alice-3290` | mobygames logo launchbox logo | +| Altair 680 | `altair-680` | mobygames logo | +| Altair 8800 | `altair-8800` | mobygames logo hasheous logo | +| Amazon Alexa | `amazon-alexa` | mobygames logo | +| Amazon Fire TV | `amazon-fire-tv` | igdb logo mobygames logo | +| Amiga | `amiga` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Amiga CD | `amiga-cd` | screenscraper logo | +| Amiga CD32 | `amiga-cd32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Amstrad CPC | `acpc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Amstrad GX4000 | `amstrad-gx4000` | igdb logo screenscraper logo launchbox logo hasheous logo | +| Amstrad PCW | `amstrad-pcw` | igdb logo mobygames logo hasheous logo | +| Analogue electronics | `analogueelectronics` | igdb logo | +| Android | `android` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Antstream | `antstream` | mobygames logo | +| APF MP1000/Imagination Machine | `apf` | mobygames logo launchbox logo hasheous logo | +| Apogee BK-01 | `bk-01` | launchbox logo | +| Apple I | `apple` | screenscraper logo mobygames logo hasheous logo | +| Apple II | `appleii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| Apple IIGS | `apple-iigs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Apple III | `appleiii` | screenscraper logo hasheous logo | +| Apple Lisa | `apple-lisa` | hasheous logo | +| Apple Pippin | `apple-pippin` | igdb logo hasheous logo | +| Arcade | `arcade` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| Arcadia 2001 | `arcadia-2001` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo | +| Arduboy | `arduboy` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | +| Astral 2000 | `astral-2000` | mobygames logo | +| Atari 2600 | `atari2600` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari 5200 | `atari5200` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Atari 7800 | `atari7800` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari 8-bit | `atari8bit` | igdb logo screenscraper logo mobygames logo hasheous logo howlongtobeat logo | +| Atari 800 | `atari800` | screenscraper logo launchbox logo | +| Atari Jaguar | `jaguar` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari Jaguar CD | `atari-jaguar-cd` | igdb logo screenscraper logo launchbox logo retroachivements logo howlongtobeat logo | +| Atari Lynx | `lynx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari ST/STE | `atari-st` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Atari VCS | `atari-vcs` | mobygames logo | +| Atari XEGS | `atari-xegs` | screenscraper logo launchbox logo | +| Atom | `atom` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| AY-3-8500 | `ay-3-8500` | igdb logo | +| AY-3-8603 | `ay-3-8603` | igdb logo | +| AY-3-8605 | `ay-3-8605` | igdb logo | +| AY-3-8606 | `ay-3-8606` | igdb logo | +| AY-3-8607 | `ay-3-8607` | igdb logo | +| AY-3-8610 | `ay-3-8610` | igdb logo | +| AY-3-8710 | `ay-3-8710` | igdb logo | +| AY-3-8760 | `ay-3-8760` | igdb logo | +| Bada | `bada` | mobygames logo | +| Bally Astrocade | `astrocade` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| BBC Microcomputer System | `bbcmicro` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Benesse Pocket Challenge V2 | `pocket-challenge-v2` | screenscraper logo hasheous logo | +| Benesse Pocket Challenge W | `pocket-challenge-w` | hasheous logo | +| BeOS | `beos` | mobygames logo | +| BGR Computers Excalibur 64 | `excalibur-64` | hasheous logo | +| Bit Corporation BIT 90 | `bit-90` | hasheous logo | +| Black Point | `black-point` | | +| BlackBerry OS | `blackberry` | igdb logo mobygames logo | +| Blacknut | `blacknut` | mobygames logo | +| Blu-ray Player | `blu-ray-player` | igdb logo mobygames logo | +| BREW | `brew` | mobygames logo | +| Browser (Flash/HTML5) | `browser` | igdb logo mobygames logo launchbox logo flashpoint logo howlongtobeat logo | +| Bubble | `bubble` | mobygames logo | +| Call-A-Computer time-shared mainframe computer system | `call-a-computer` | igdb logo | +| Cambridge Computer Z88 | `z88` | hasheous logo | +| Camputers Lynx | `camputers-lynx` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| Capcom Play System | `cps1` | screenscraper logo | +| Capcom Play System 2 | `cps2` | screenscraper logo | +| Capcom Play System 3 | `cps3` | screenscraper logo | +| Casio CFX-9850 | `casio-cfx-9850` | hasheous logo | +| Casio FP-1000 & FP-1100 | `casio-fp-1000` | hasheous logo | +| Casio Loopy | `casio-loopy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Casio PB-1000 | `casio-pb-1000` | hasheous logo | +| Casio Programmable Calculator | `casio-programmable-calculator` | mobygames logo | +| Casio PV-1000 | `casio-pv-1000` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| Casio PV-2000 | `casio-pv-2000` | hasheous logo | +| CDC Cyber 70 | `cdccyber70` | igdb logo | +| Champion 2711 | `champion-2711` | mobygames logo | +| ClickStart | `clickstart` | mobygames logo | +| Coleco Adam | `colecoadam` | screenscraper logo mobygames logo launchbox logo | +| ColecoVision | `colecovision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Colour Genie | `colour-genie` | screenscraper logo mobygames logo launchbox logo | +| Commodore 128 | `c128` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| Commodore 16 | `c16` | igdb logo screenscraper logo mobygames logo hasheous logo | +| Commodore C64/128/MAX | `c64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Commodore CDTV | `commodore-cdtv` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Commodore PET | `cpet` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Commodore Plus/4 | `c-plus-4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Commodore VIC-20 | `vic-20` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | +| Compal 80 | `compal-80` | mobygames logo | +| Compucolor I | `compucolor-i` | mobygames logo | +| Compucolor II | `compucolor-ii` | mobygames logo | +| Compucorp Programmable Calculator | `compucorp-programmable-calculator` | mobygames logo | +| COSMAC | `fred-cosmac` | mobygames logo | +| CP/M | `cpm` | mobygames logo | +| CreatiVision | `creativision` | screenscraper logo mobygames logo launchbox logo | +| Cybervision | `cybervision` | mobygames logo | +| Danger OS | `danger-os` | mobygames logo | +| Daydream | `daydream` | igdb logo | +| DEC GT40 | `gt40` | igdb logo | +| Dedicated console | `dedicated-console` | mobygames logo | +| Dedicated handheld | `dedicated-handheld` | mobygames logo | +| Didj | `didj` | mobygames logo | +| Digiblast | `digiblast` | igdb logo mobygames logo | +| DoJa | `doja` | mobygames logo | +| Donner Model 30 | `donner30` | igdb logo | +| DOS | `dos` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | +| Dragon 32/64 | `dragon-32-slash-64` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Dreamcast | `dc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| DVD Player | `dvd-player` | igdb logo mobygames logo howlongtobeat logo | +| e-Reader / Card-e Reader | `e-reader-slash-card-e-reader` | igdb logo | +| ECD Micromind | `ecd-micromind` | mobygames logo | +| EDSAC | `edsac` | igdb logo | +| Elektor TV Games Computer | `elektor` | igdb logo retroachivements logo | +| Elektronika BK | `bk` | screenscraper logo launchbox logo | +| Enterprise | `enterprise` | mobygames logo launchbox logo | +| Epoch Cassette Vision | `epoch-cassette-vision` | igdb logo mobygames logo | +| Epoch Game Pocket Computer | `epoch-game-pocket-computer` | screenscraper logo mobygames logo launchbox logo | +| Epoch Super Cassette Vision | `epoch-super-cassette-vision` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Evercade | `evercade` | igdb logo mobygames logo howlongtobeat logo | +| Exelvision | `exelvision` | screenscraper logo mobygames logo launchbox logo | +| ExEn | `exen` | mobygames logo | +| Exidy Sorcerer | `exidy-sorcerer` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Fairchild Channel F | `fairchild-channel-f` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Family Computer | `famicom` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo libretro logo | +| Family Computer Disk System | `fds` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | +| Feature phone | `mobile-custom` | mobygames logo | +| Ferranti Nimrod Computer | `nimrod` | igdb logo | +| FM Towns | `fm-towns` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| FM-7 | `fm-7` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| Freebox | `freebox` | mobygames logo | +| G-cluster | `g-cluster` | mobygames logo | +| Galaksija | `galaksija` | mobygames logo | +| Gamate | `gamate` | igdb logo screenscraper logo hasheous logo | +| Game & Watch | `g-and-w` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| Game Boy | `gb` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Game Boy Advance | `gba` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Game Boy Color | `gbc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Game Master | `hartung` | screenscraper logo launchbox logo | +| Game Wave | `game-wave` | mobygames logo launchbox logo | +| Game.com | `game-dot-com` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| GameStick | `gamestick` | mobygames logo | +| Gear VR | `gear-vr` | igdb logo howlongtobeat logo | +| GIMINI | `gimini` | mobygames logo | +| Gizmondo | `gizmondo` | igdb logo mobygames logo howlongtobeat logo | +| Gloud | `gloud` | mobygames logo | +| Glulx | `glulx` | mobygames logo | +| GNEX | `gnex` | mobygames logo | +| Google Stadia | `stadia` | igdb logo mobygames logo howlongtobeat logo | +| GP2X | `gp2x` | mobygames logo | +| GP2X Wiz | `gp2x-wiz` | mobygames logo | +| GP32 | `gp32` | screenscraper logo mobygames logo launchbox logo | +| GVM | `gvm` | mobygames logo | +| Handheld Electronic LCD | `handheld-electronic-lcd` | igdb logo | +| HD DVD Player | `hd-dvd-player` | mobygames logo | +| Heath/Zenith H8/H89 | `heathzenith` | mobygames logo | +| Heathkit H11 | `heathkit-h11` | mobygames logo | +| Hector HRX | `hrx` | launchbox logo | +| Hitachi S1 | `hitachi-s1` | mobygames logo | +| HP 2100 | `hp2100` | igdb logo | +| HP 3000 | `hp3000` | igdb logo | +| HP 9800 | `hp-9800` | mobygames logo | +| HP Programmable Calculator | `hp-programmable-calculator` | mobygames logo | +| Hugo | `hugo` | mobygames logo | +| Hyper Neo Geo 64 | `hyper-neo-geo-64` | igdb logo | +| HyperScan | `hyperscan` | igdb logo mobygames logo launchbox logo | +| IBM 5100 | `ibm-5100` | mobygames logo | +| IBM PCjr | `pc-jr` | hasheous logo | +| Ideal-Computer | `ideal-computer` | mobygames logo | +| iiRcade | `iircade` | mobygames logo | +| Imlac PDS-1 | `imlac-pds1` | igdb logo | +| Intel 8008 | `intel-8008` | mobygames logo | +| Intel 8080 | `intel-8080` | mobygames logo | +| Intel 8086 / 8088 | `intel-8086` | mobygames logo | +| Intellivision | `intellivision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Intellivision Amico | `intellivision-amico` | igdb logo | +| Interact Model One | `interact-model-one` | mobygames logo | +| Interton VC 4000 | `interton-vc-4000` | retroachivements logo | +| Interton Video 2000 | `interton-video-2000` | mobygames logo | +| iOS | `ios` | igdb logo mobygames logo launchbox logo | +| iPad | `ipad` | mobygames logo | +| iPod Classic | `ipod-classic` | mobygames logo | +| J2ME | `j2me` | mobygames logo | +| Jolt | `jolt` | mobygames logo | +| Jupiter Ace | `jupiter-ace` | screenscraper logo mobygames logo launchbox logo | +| KaiOS | `kaios` | mobygames logo | +| KIM-1 | `kim-1` | mobygames logo | +| Kindle Classic | `kindle` | mobygames logo | +| Laser 200 | `laser200` | mobygames logo | +| LaserActive | `laseractive` | igdb logo mobygames logo | +| LeapFrog Explorer | `leapfrog-explorer` | mobygames logo | +| Leapster | `leapster` | igdb logo mobygames logo | +| Leapster Explorer/LeadPad Explorer | `leapster-explorer-slash-leadpad-explorer` | igdb logo mobygames logo | +| LeapTV | `leaptv` | igdb logo mobygames logo | +| Legacy Computer | `legacy-computer` | igdb logo | +| Legacy Mobile Device | `mobile` | igdb logo howlongtobeat logo | +| Linux | `linux` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Luna | `luna` | mobygames logo howlongtobeat logo | +| Mac | `mac` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Maemo | `maemo` | mobygames logo | +| Magnavox Odyssey | `odyssey` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Mainframe | `mainframe` | mobygames logo | +| Matsushita/Panasonic JR | `matsushitapanasonic-jr` | mobygames logo | +| Mattel Aquarius | `aquarius` | mobygames logo launchbox logo hasheous logo | +| MeeGo | `meego` | mobygames logo | +| Mega Duck/Cougar Boy | `mega-duck-slash-cougar-boy` | igdb logo launchbox logo retroachivements logo | +| Memotech MTX | `memotech-mtx` | mobygames logo | +| Memotech MTX512 | `mtx512` | launchbox logo | +| Meritum | `meritum` | mobygames logo | +| Meta Quest 2 | `meta-quest-2` | igdb logo | +| Meta Quest 3 | `meta-quest-3` | igdb logo | +| Microbee | `microbee` | mobygames logo hasheous logo | +| Microcomputer | `microcomputer` | igdb logo | +| Microsoft MSX2+ | `msx2plus` | screenscraper logo launchbox logo | +| Microtan 65 | `microtan-65` | mobygames logo | +| Microvision | `microvision` | igdb logo mobygames logo | +| Mophun | `mophun` | mobygames logo | +| MOS Technology 6502 | `mos-technology-6502` | mobygames logo | +| Motorola 6800 | `motorola-6800` | mobygames logo | +| Motorola 68k | `motorola-68k` | mobygames logo | +| MRE | `mre` | mobygames logo | +| MSX | `msx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| MSX Turbo R | `msx-turbo` | screenscraper logo | +| MSX2 | `msx2` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | +| MUGEN | `mugen` | launchbox logo | +| N-Gage | `ngage` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| N-Gage (service) | `ngage2` | mobygames logo | +| Namco System 22 | `system-32` | screenscraper logo launchbox logo | +| Nascom | `nascom` | mobygames logo | +| NEC PC-6000 Series | `nec-pc-6000-series` | igdb logo hasheous logo | +| Neo Geo AES | `neogeoaes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Neo Geo CD | `neo-geo-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Neo Geo MVS | `neogeomvs` | igdb logo screenscraper logo mobygames logo launchbox logo libretro logo | +| Neo Geo Pocket | `neo-geo-pocket` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Neo Geo Pocket Color | `neo-geo-pocket-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Neo Geo X | `neo-geo-x` | mobygames logo | +| New Nintendo 3DS | `new-nintendo-3ds` | igdb logo mobygames logo hasheous logo | +| NewBrain | `newbrain` | mobygames logo | +| Newton | `newton` | mobygames logo | +| Nintendo 3DS | `3ds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Nintendo 64 | `n64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo 64DD | `64dd` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | +| Nintendo DS | `nds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo DSi | `nintendo-dsi` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | +| Nintendo Entertainment System | `nes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo GameCube | `ngc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo Switch | `switch` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Nintendo Switch 2 | `switch-2` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| North Star | `northstar` | mobygames logo | +| Noval 760 | `noval-760` | mobygames logo | +| Nuon | `nuon` | igdb logo mobygames logo launchbox logo | +| Oculus Go | `oculus-go` | igdb logo mobygames logo howlongtobeat logo | +| Oculus Quest | `oculus-quest` | igdb logo mobygames logo howlongtobeat logo | +| Oculus Rift | `oculus-rift` | igdb logo | +| Oculus VR | `oculus-vr` | igdb logo | +| Odyssey 2 / Videopac G7000 | `odyssey-2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Ohio Scientific | `ohio-scientific` | mobygames logo | +| OnLive Game System | `onlive-game-system` | igdb logo mobygames logo howlongtobeat logo | +| OOParts | `ooparts` | igdb logo mobygames logo | +| OpenBOR | `openbor` | screenscraper logo launchbox logo | +| Orao | `orao` | mobygames logo | +| Oric | `oric` | screenscraper logo mobygames logo | +| Oric Atmos | `atmos` | screenscraper logo launchbox logo | +| OS/2 | `os2` | mobygames logo | +| Othello Multivision | `multivision` | launchbox logo hasheous logo | +| Ouya | `ouya` | igdb logo mobygames logo launchbox logo howlongtobeat logo | +| Palm OS | `palm-os` | igdb logo screenscraper logo mobygames logo | +| Palmtex | `palmtex` | | +| Panasonic Jungle | `panasonic-jungle` | igdb logo | +| Panasonic M2 | `panasonic-m2` | igdb logo | +| Pandora | `pandora` | mobygames logo | +| PC Booter | `pc-booter` | mobygames logo | +| PC Engine SuperGrafx | `supergrafx` | igdb logo screenscraper logo mobygames logo launchbox logo libretro logo | +| PC-50X Family | `pc-50x-family` | igdb logo | +| PC-6001 | `pc-6001` | mobygames logo | +| PC-8000 | `pc-8000` | mobygames logo | +| PC-8800 Series | `pc-8800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PC-9800 Series | `pc-9800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| PC-FX | `pc-fx` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo howlongtobeat logo libretro logo | +| PDP-1 | `pdp1` | igdb logo | +| PDP-10 | `pdp10` | igdb logo | +| PDP-11 | `pdp11` | igdb logo | +| PDP-7 | `pdp-7` | igdb logo | +| PDP-8 | `pdp-8` | igdb logo | +| Pebble | `pebble` | mobygames logo | +| Philips CD-i | `philips-cd-i` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Philips VG 5000 | `philips-vg-5000` | screenscraper logo mobygames logo launchbox logo | +| Photo CD | `photocd` | mobygames logo | +| PICO | `pico` | screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| Pinball | `pinball` | screenscraper logo launchbox logo | +| Pippin | `pippin` | mobygames logo | +| PLATO | `plato` | igdb logo | +| Playdate | `playdate` | igdb logo mobygames logo howlongtobeat logo | +| Playdia | `playdia` | igdb logo mobygames logo | +| PlayStation | `psx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PlayStation 2 | `ps2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PlayStation 3 | `ps3` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation 4 | `ps4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation 5 | `ps5` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation Now | `playstation-now` | mobygames logo howlongtobeat logo | +| PlayStation Portable | `psp` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PlayStation Vita | `psvita` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation VR | `psvr` | igdb logo howlongtobeat logo | +| PlayStation VR2 | `psvr2` | igdb logo | +| Plex Arcade | `plex-arcade` | mobygames logo | +| Plug & Play | `plug-and-play` | igdb logo howlongtobeat logo | +| PocketStation | `pocketstation` | igdb logo launchbox logo hasheous logo | +| Pokitto | `pokitto` | mobygames logo | +| Pokémon mini | `pokemon-mini` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Poly-88 | `poly-88` | mobygames logo | +| Polymega | `polymega` | igdb logo | +| PSP Minis | `psp-minis` | screenscraper logo launchbox logo | +| R-Zone | `r-zone` | igdb logo | +| RCA Studio II | `rca-studio-ii` | mobygames logo launchbox logo hasheous logo | +| Research Machines 380Z | `research-machines-380z` | mobygames logo | +| Roku | `roku` | mobygames logo | +| SAM Coupé | `sam-coupe` | screenscraper logo mobygames logo launchbox logo | +| Satellaview | `satellaview` | igdb logo screenscraper logo launchbox logo libretro logo | +| SC/MP | `scmp` | mobygames logo | +| ScummVM | `scummvm` | igdb logo screenscraper logo launchbox logo libretro logo | +| SD-200/270/290 | `sd-200270290` | mobygames logo | +| SDS Sigma 7 | `sdssigma7` | igdb logo | +| Sega 32X | `sega32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Advanced Pico Beena | `beena` | hasheous logo | +| Sega CD | `segacd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega CD 32X | `segacd32` | igdb logo launchbox logo | +| Sega Dreamcast VMU | `vmu` | launchbox logo | +| Sega Game Gear | `gamegear` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Hikaru | `hikaru` | screenscraper logo launchbox logo | +| Sega Master System/Mark III | `sms` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Mega Drive/Genesis | `genesis` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Model 1 | `model1` | launchbox logo | +| Sega Model 2 | `model2` | screenscraper logo launchbox logo | +| Sega Model 3 | `model3` | screenscraper logo launchbox logo | +| Sega Pico | `sega-pico` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Sega Saturn | `saturn` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega SC-3000 | `sc3000` | launchbox logo hasheous logo | +| Sega ST-V | `stv` | screenscraper logo launchbox logo | +| Sega System 16 | `system16` | launchbox logo | +| Sega System 32 | `system32` | launchbox logo | +| SG-1000 | `sg1000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sharp MZ-2200 | `sharp-mz-2200` | igdb logo | +| Sharp MZ-80B/2000/2500 | `sharp-mz-80b20002500` | mobygames logo launchbox logo | +| Sharp MZ-80K/700/800/1500 | `sharp-mz-80k7008001500` | mobygames logo | +| Sharp X1 | `x1` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Sharp X68000 | `sharp-x68000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Sharp Zaurus | `sharp-zaurus` | mobygames logo | +| Signetics 2650 | `signetics-2650` | mobygames logo | +| Sinclair QL | `sinclair-ql` | igdb logo mobygames logo hasheous logo | +| Sinclair ZX81 | `zx81` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| SK-VM | `sk-vm` | mobygames logo | +| SMC-777 | `smc-777` | mobygames logo | +| Socrates | `socrates` | mobygames logo launchbox logo | +| Sol-20 | `sol-20` | igdb logo mobygames logo | +| Sord M5 | `sord-m5` | mobygames logo launchbox logo | +| Spectravideo | `spectravideo` | screenscraper logo mobygames logo launchbox logo | +| SRI-500/1000 | `sri-5001000` | mobygames logo | +| SteamVR | `steam-vr` | igdb logo | +| Sufami Turbo | `sufami-turbo` | screenscraper logo libretro logo | +| Super A'Can | `super-acan` | igdb logo screenscraper logo mobygames logo | +| Super Famicom | `sfam` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Super NES CD-ROM System | `super-nes-cd-rom-system` | igdb logo | +| Super Nintendo Entertainment System | `snes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Super Vision 8000 | `super-vision-8000` | mobygames logo launchbox logo hasheous logo | +| Sure Shot HD | `sure-shot-hd` | mobygames logo | +| SwanCrystal | `swancrystal` | igdb logo | +| SWTPC 6800 | `swtpc-6800` | mobygames logo | +| Symbian | `symbian` | mobygames logo | +| TADS | `tads` | mobygames logo | +| Taito X-55 | `taito-x-55` | screenscraper logo mobygames logo | +| Tandy Vis | `tandy-vis` | | +| Tapwave Zodiac | `zod` | igdb logo launchbox logo | +| Tatung Einstein | `tatung-einstein` | igdb logo mobygames logo | +| Tektronix 4050 | `tektronix-4050` | mobygames logo | +| Tele-Spiel ES-2201 | `tele-spiel` | mobygames logo | +| Telstar Arcade | `telstar-arcade` | mobygames logo | +| Terebikko / See 'n Say Video Phone | `terebikko-slash-see-n-say-video-phone` | igdb logo | +| Terminal | `terminal` | mobygames logo | +| Texas Instruments TI-82 | `ti-82` | hasheous logo | +| Texas Instruments TI-83 | `ti-83` | hasheous logo | +| Texas Instruments TI-99 | `ti-99` | igdb logo screenscraper logo mobygames logo | +| Thomson MO5 | `thomson-mo5` | igdb logo screenscraper logo mobygames logo | +| Thomson TO | `thomson-to` | screenscraper logo mobygames logo | +| TI Programmable Calculator | `ti-programmable-calculator` | mobygames logo | +| TI-99/4A | `ti-994a` | screenscraper logo mobygames logo launchbox logo | +| TIC-80 | `tic-80` | screenscraper logo libretro logo | +| Tiki 100 | `tiki-100` | mobygames logo | +| TIM | `tim` | mobygames logo | +| Timex Sinclair 2068 | `timex-sinclair-2068` | mobygames logo | +| Tizen | `tizen` | mobygames logo | +| Tomahawk F1 | `tomahawk-f1` | mobygames logo | +| Tomy Tutor | `tomy-tutor` | mobygames logo launchbox logo libretro logo | +| Tomy Tutor / Pyuta / Grandstand Tutor | `tomy-tutor-slash-pyuta-slash-grandstand-tutor` | igdb logo | +| Triton | `triton` | mobygames logo | +| TRS-80 | `trs-80` | igdb logo mobygames logo launchbox logo hasheous logo | +| TRS-80 Color Computer | `trs-80-color-computer` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| TRS-80 MC-10 | `trs-80-mc-10` | mobygames logo | +| TRS-80 Model 100 | `trs-80-model-100` | mobygames logo | +| TurboGrafx-16/PC Engine | `tg16` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Turbografx-16/PC Engine CD | `turbografx-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| tvOS | `tvos` | mobygames logo | +| Type X | `type-x` | screenscraper logo launchbox logo | +| Uzebox | `uzebox` | igdb logo screenscraper logo retroachivements logo | +| V.Flash | `vflash` | mobygames logo | +| V.Smile | `vsmile` | igdb logo screenscraper logo mobygames logo launchbox logo | +| VC 4000 | `vc-4000` | igdb logo screenscraper logo launchbox logo | +| Vector-06C | `06c` | launchbox logo | +| Vectrex | `vectrex` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Versatile | `versatile` | mobygames logo | +| VideoBrain | `videobrain` | mobygames logo | +| Videopac+ G7400 | `videopac-g7400` | screenscraper logo mobygames logo launchbox logo | +| Virtual Boy | `virtualboy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Virtual Console | `vc` | igdb logo | +| VIS | `vis` | mobygames logo | +| visionOS | `visionos` | igdb logo | +| Visual Memory Unit / Visual Memory System | `visual-memory-unit-slash-visual-memory-system` | igdb logo | +| Wang 2200 | `wang2200` | mobygames logo | +| WASM-4 | `wasm-4` | screenscraper logo retroachivements logo | +| Watara/QuickShot Supervision | `supervision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| watchOS | `watchos` | mobygames logo | +| webOS | `webos` | mobygames logo | +| Wii | `wii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Wii U | `wiiu` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Windows | `win` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| Windows 3.x | `win3x` | screenscraper logo mobygames logo launchbox logo | +| Windows Apps | `windows-apps` | mobygames logo | +| Windows Mixed Reality | `windows-mixed-reality` | igdb logo | +| Windows Mobile | `windows-mobile` | igdb logo mobygames logo | +| Windows Phone | `winphone` | igdb logo mobygames logo howlongtobeat logo | +| WIPI | `wipi` | mobygames logo | +| WonderSwan | `wonderswan` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| WonderSwan Color | `wonderswan-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| XaviXPORT | `xavixport` | mobygames logo launchbox logo | +| Xbox | `xbox` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Xbox 360 | `xbox360` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Xbox Cloud Gaming | `xboxcloudgaming` | mobygames logo | +| Xbox One | `xboxone` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Xbox Series X/S | `series-x-s` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Xerox Alto | `xerox-alto` | mobygames logo | +| Z-machine | `z-machine` | screenscraper logo mobygames logo | +| Zeebo | `zeebo` | igdb logo mobygames logo howlongtobeat logo | +| Zilog Z80 | `z80` | mobygames logo | +| Zilog Z8000 | `zilog-z8000` | mobygames logo | +| ZiNc | `zinc` | launchbox logo | +| Zodiac | `zodiac` | mobygames logo | +| Zune | `zune` | mobygames logo | +| ZX Spectrum | `zxs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| ZX Spectrum Next | `zx-spectrum-next` | mobygames logo | +| ZX80 | `zx80` | mobygames logo hasheous logo | diff --git a/docs/scripts/__pycache__/_sources.cpython-311.pyc b/docs/scripts/__pycache__/_sources.cpython-311.pyc deleted file mode 100644 index fa9d11738fe01969f5b2b7d5d2d91e31c8f81245..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2714 zcmbVOO>7fK6rSB3+wo6q$d3!DNX$>6PMR3RPf-PJQ=nDa;-m>xg^{{4o(bMy@4B;V zLV^(%s?<_NYQ%*|sFKwKrGi6_RaL3tQuSy@mST;B6qQqMsptXa(!N>$#LLg6v$JpB zzIi*Iec$`$`KQiKAA**={Y3L&gr10pW>e~l^z<)K?jV8^h)6`vNLe`{OF~yNN;gJwkW;&Y75Z7gw(3$X1GTww&$F}B6cx6OA)PGMx=mFgIRthw=_VFG( zJZKqaI-6=DNQ1;Z??)&RqQNaY@dz8~uxGZBUgG~6C$^I?3BuS#UL)IJ>?S)%4~#uT znL^Q>^CMH~B%>sfrkNaNW@Or65!1-C6dg9Ehb@-3(veG4rz~k1%p6%4n}|x>pVW23 zN?Mwsn{jx?6G(ncU5&m1jD~L`zvI zv2wGyL4l>Dr?)}5gRG_<#!@S4ZAL)G8PTKYIRvMqWDzPLr)NqgfHOh&WQaE0 z^(LVyv=<#hCZ3hAD(BIxB%$+2f{VaRRE~3*U5sKLm>U0l^3<6r^~14G&v0zysLnm& zi!<6qjx$D*KyajKQ*1i2`DQF_WN9pKQWhgd%B%}B#k8Dd#?)rSa&tT&UTqp!H@q!w z^CpNQdhG38_4ciJ`<9MWypgImvVkPmKxy=GD7;Wu8ohO6;l_pn7Tdff{uW;U2(e{* znfC12vzM8-Y-R|yakuA$@*5sSo=1uVlH(j?09Qh^oM=a~ZO)nB*QJ6)q@Cbi zZ$#eGuglNPDQ$BK!3Fs)BCdjB%U5NH%ZdA4^n)9abjLYnMm7tQ<_xYdIz7K9ZCN?< zomh+|XGbn+Ryu!CK$bFei|W=$%E-nxrC3z9apCR^e6yB|V%7sY=bi?NYzI6su}>3D z85DuWx`abO{G19)t9h1TyTBwo&Cfspw86ex`hs5ksOIllJht%O(y^+4Pw{lkw|#N{ zLTu@csxMMJRr8N8wO2g3y=-KE43)r^cABMDL%i6 znQxj}skCa*s}>ssyKpn_fDmL1E%dK?2UffTCFTBP**j429;|u~3c3s}pRR?vzn}YV z?$))1YpbE%E1}(`1C`KVH8dy~I%GR-3Bk}I!O-U?wp?tj!O&NGncHD+KOPtjE04l> zw9oTsmjseyJ(;E4(@10{z(}>DEDSbTi=a63qSVR@m})gBYA6XA zf&%#vJM&3G1sSO6il2+h+>?X)q(imnpjS21QjN=OFZ@2>+(C6fP17$$6*!0EV-pi+ z&P}PO$0oTedzomA`(~MDQ5&_W!iL~2_7(`PKysKOTU(s$ta~;p6FUlX``|X;2LTY{ zZL2uEg2PMJeNx8Z3O-!Lhs*fzV_$fwr{dcMl*FEI;y2@qZ)H%lszX>}egeX(?p=R^Mj^xkC4uO68ZFZc<8YG{g@3IfT zQzVu7F9=|RB-PN7GCHz`_LQA{4f%@pRYSeyX0M?gWoKVg+*PG}adt)NFDv~u+*OR% zuQmL7x!Ko(UEh+$ - - -_Supported-platforms table is generated at build time. Run -`uv run python docs/scripts/gen_platforms.py` to refresh._ -""" - - -def main() -> int: - out = write_snippet("supported-platforms.md", PLACEHOLDER) - print(f"Wrote placeholder to {out}") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/pyproject.toml b/pyproject.toml index 949f80a3..c733d2a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ "cairosvg>=2.7.1", "mike>=2.1.3", "mkdocs>=1.6.1", + "mkdocs-asciinema-player>=0.16.0", "mkdocs-git-revision-date-localized-plugin>=1.3.0", "mkdocs-glightbox>=0.4.0", "mkdocs-literate-nav>=0.6.1", @@ -17,6 +18,6 @@ dependencies = [ "mkdocs-video>=1.5.0", "neoteroi-mkdocs>=1.1.0", "pillow>=11.1.0", - "mkdocs-asciinema-player>=0.16.0", + "python-dotenv>=1.0.0", "tomli>=2.0.1", ] diff --git a/docs/scripts/_sources.py b/scripts/_sources.py similarity index 79% rename from docs/scripts/_sources.py rename to scripts/_sources.py index 357125d9..9dd62362 100644 --- a/docs/scripts/_sources.py +++ b/scripts/_sources.py @@ -2,18 +2,19 @@ from __future__ import annotations -import sys import urllib.request +import tomllib from pathlib import Path -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib SCRIPTS_DIR = Path(__file__).resolve().parent +REPO_ROOT = SCRIPTS_DIR.parent +DOCS_DIR = REPO_ROOT / "docs" +SNIPPETS_DIR = DOCS_DIR / "resources" / "snippets" SOURCES_FILE = SCRIPTS_DIR / "sources.toml" -SNIPPETS_DIR = SCRIPTS_DIR.parent / "resources" / "snippets" +MKDOCS_YML = REPO_ROOT / "mkdocs.yml" +SITE_DIR = REPO_ROOT / "site" +ENV_FILE = REPO_ROOT / ".env" def load_sources() -> dict: diff --git a/docs/scripts/check_redirects.py b/scripts/check_redirects.py similarity index 90% rename from docs/scripts/check_redirects.py rename to scripts/check_redirects.py index 1cdc463e..aba6813d 100644 --- a/docs/scripts/check_redirects.py +++ b/scripts/check_redirects.py @@ -4,19 +4,16 @@ Run manually: uv run mkdocs build - uv run python docs/scripts/check_redirects.py + uv run python -m scripts.check_redirects """ from __future__ import annotations import sys -from pathlib import Path import yaml -REPO_ROOT = Path(__file__).resolve().parents[2] -MKDOCS_YML = REPO_ROOT / "mkdocs.yml" -SITE_DIR = REPO_ROOT / "site" +from scripts._sources import MKDOCS_YML, SITE_DIR class _LooseLoader(yaml.SafeLoader): diff --git a/docs/scripts/gen_env_vars.py b/scripts/gen_env_vars.py similarity index 95% rename from docs/scripts/gen_env_vars.py rename to scripts/gen_env_vars.py index 3c072d1f..980fc6a6 100644 --- a/docs/scripts/gen_env_vars.py +++ b/scripts/gen_env_vars.py @@ -6,7 +6,7 @@ --8<-- "env-vars.md" Run manually: - uv run python docs/scripts/gen_env_vars.py + uv run python -m scripts.gen_env_vars The env.template format this parses: @@ -30,7 +30,7 @@ from collections import defaultdict from typing import Iterable -from _sources import fetch_text, romm_raw_url, write_snippet +from scripts._sources import fetch_text, romm_raw_url, write_snippet # KEY=VALUE, followed optionally by ` # comment`. Value may contain spaces. VAR_LINE_RE = re.compile(r"^([A-Z][A-Z0-9_]*)=(.*)$") @@ -102,7 +102,7 @@ def render(rows: Iterable[dict]) -> str: grouped[r["section"]].append(r) out: list[str] = [] - out.append("") + out.append("") out.append("") for section, items in grouped.items(): diff --git a/scripts/gen_platforms.py b/scripts/gen_platforms.py new file mode 100644 index 00000000..983a1ae9 --- /dev/null +++ b/scripts/gen_platforms.py @@ -0,0 +1,73 @@ +"""Generate the supported-platforms table from a local rommapp/romm checkout. + +Output: docs/resources/snippets/supported-platforms.md + +Strategy: shell out to the upstream generator at +`backend/utils/generate_supported_platforms.py`. That module imports the +full romm handler stack (redis, db, httpx), so it has to run inside +romm's own uv environment, not this one. + +Set `ROMM_SRC` to the romm checkout root (read from .env at the repo root +or from the process environment). + +Run manually: + uv run python -m scripts.gen_platforms +""" + +from __future__ import annotations + +import os +import subprocess +import sys +from pathlib import Path + +from dotenv import load_dotenv + +from scripts._sources import ENV_FILE, write_snippet + +HEADER = "\n" + +load_dotenv(ENV_FILE) + + +def main() -> int: + romm_src = os.environ.get("ROMM_SRC") + if not romm_src: + print( + f"ROMM_SRC not set (add it to {ENV_FILE} or the environment)", + file=sys.stderr, + ) + return 1 + + backend = Path(romm_src).expanduser() / "backend" + script = backend / "utils" / "generate_supported_platforms.py" + if not script.exists(): + print(f"error: {script} not found (ROMM_SRC={romm_src})", file=sys.stderr) + return 1 + + result = subprocess.run( + ["uv", "run", "python", "-m", "utils.generate_supported_platforms"], + cwd=backend, + capture_output=True, + text=True, + ) + if result.returncode != 0: + sys.stderr.write(result.stderr) + return result.returncode + + lines = result.stdout.splitlines() + try: + start = next(i for i, line in enumerate(lines) if line.startswith("|Platform")) + except StopIteration: + print("error: could not find table header in upstream output", file=sys.stderr) + return 1 + + table = "\n".join(lines[start:]) + "\n" + rows = sum(1 for line in lines[start:] if line.startswith("| ")) + out = write_snippet("supported-platforms.md", HEADER + "\n" + table) + print(f"Wrote {rows} platforms to {out}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/gen_scheduled_tasks.py b/scripts/gen_scheduled_tasks.py similarity index 95% rename from docs/scripts/gen_scheduled_tasks.py rename to scripts/gen_scheduled_tasks.py index 88cca18b..32727a06 100644 --- a/docs/scripts/gen_scheduled_tasks.py +++ b/scripts/gen_scheduled_tasks.py @@ -8,12 +8,12 @@ shape stabilizes for 5.0. Run manually: - uv run python docs/scripts/gen_scheduled_tasks.py + uv run python -m scripts.gen_scheduled_tasks """ from __future__ import annotations -from _sources import write_snippet +from scripts._sources import write_snippet # Sourced from rommapp/romm@master backend/tasks/* registration as of 4.8.x. @@ -101,7 +101,7 @@ def render() -> str: out = [ - "", + "", "", "| Task | Type | Default schedule | Env var | Purpose |", "| --- | --- | --- | --- | --- |", diff --git a/docs/scripts/sources.toml b/scripts/sources.toml similarity index 100% rename from docs/scripts/sources.toml rename to scripts/sources.toml diff --git a/uv.lock b/uv.lock index 45427146..71b9e458 100644 --- a/uv.lock +++ b/uv.lock @@ -770,6 +770,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + [[package]] name = "pytz" version = "2025.1" @@ -901,6 +910,7 @@ dependencies = [ { name = "mkdocs-video" }, { name = "neoteroi-mkdocs" }, { name = "pillow" }, + { name = "python-dotenv" }, { name = "tomli" }, ] @@ -919,6 +929,7 @@ requires-dist = [ { name = "mkdocs-video", specifier = ">=1.5.0" }, { name = "neoteroi-mkdocs", specifier = ">=1.1.0" }, { name = "pillow", specifier = ">=11.1.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "tomli", specifier = ">=2.0.1" }, ] From d8381280f287bd4103c6c6915f7f6dec7d8582c7 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 18 Apr 2026 22:24:03 -0400 Subject: [PATCH 034/121] finish getting started --- docs/Navigation.md | 1 - docs/getting-started/first-scan.md | 22 ++-- docs/getting-started/what-is-new-in-5.md | 137 ----------------------- 3 files changed, 10 insertions(+), 150 deletions(-) delete mode 100644 docs/getting-started/what-is-new-in-5.md diff --git a/docs/Navigation.md b/docs/Navigation.md index b3f85c3d..0c9a1d57 100644 --- a/docs/Navigation.md +++ b/docs/Navigation.md @@ -11,7 +11,6 @@ search: - [Folder Structure](getting-started/folder-structure.md) - [Your First Scan](getting-started/first-scan.md) - [Core Concepts](getting-started/concepts.md) - - [What's New in 5.0](getting-started/what-is-new-in-5.md) - Install & Deploy - [Overview](install/index.md) - [Docker Compose](install/docker-compose.md) diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md index 3b14d9b5..97ca8599 100644 --- a/docs/getting-started/first-scan.md +++ b/docs/getting-started/first-scan.md @@ -13,23 +13,21 @@ A fifteen-second check that saves hours: - **Library is mounted**: on the host, `ls /path/to/library` should show your `roms/` (or per-platform) folders. If it doesn't, the mount is wrong. - **At least one metadata provider is configured**: RomM will scan without one, but every game comes back "unmatched" and you'll have nothing useful to look at. -- **The Setup Wizard is done**: if RomM is still showing the wizard, finish that first. The first user becomes Admin. +- **The Setup Wizard is done**: if RomM is still showing the wizard, finish that first. The first user becomes an admin. ## Run the scan 1. Click **Scan** in the sidebar. -2. Leave all platforms checked (they're auto-discovered from your folders). -3. Leave all configured providers checked. -4. Leave the mode as the default. -5. Click **Start Scan**. +2. Select the metadata sources you want to hit. +3. Set the mode to Quick Scan +4. Click **SCAN**. -The page switches to live mode: +The page switches to a live feed of the scan's progress. You can leave and come back later, or even close the browser and check back in an hour. During the scan: -- A **log** on the right streams everything the scanner is doing: file hashed, provider queried, match found or not. - Per-platform **accordion panels** show counts update live: total found, matched, unmatched. - You can click a **matched ROM** while the scan is still running to see what metadata RomM pulled, with no need to wait for the full run. -First scans on big libraries take a while. Expect ~1 second per ROM with a fast network to IGDB/ScreenScraper, and hashing (which runs unless you disabled it) adds IO time proportional to file size. +First scans on big libraries take a while. Expect ~4 seconds per ROM with a fast network to IGDB/ScreenScraper, and hashing (which runs unless you disabled it) adds IO time proportional to file size. ## What "matched" means @@ -43,9 +41,9 @@ For each ROM the scanner: An **unmatched** ROM means no provider recognised it. Common causes: - Filename is too generic (`game.gba`). -- Bad rip, intro / patch applied, or a regional variant no provider has indexed. -- Platform folder misnamed: the scanner queries providers scoped to the detected platform, so wrong platform = no results. -- Metadata provider credentials wrong or rate-limited: check the scan log for errors. +- Bad rip, intro/patch applied, or a regional variant no provider has indexed. +- Platform folder misnamed: the scanner queries providers scoped to the detected platform, so wrong platform equals no results. +- Metadata provider credentials wrong or rate-limited: check the scan log for errors or the metadata provider status page in the admin dashboard. Most of these are fixable. See [Scanning Troubleshooting](../troubleshooting/scanning.md). @@ -55,7 +53,7 @@ Click the **RomM logo** (top-left) to go home. You should see: - Platform cards for each folder it scanned. - A **Recently Added** carousel on the dashboard. -- A **Continue Playing** section: empty until you play something. +- A **Continue Playing** section (empty until you play something). From here, typical next steps: diff --git a/docs/getting-started/what-is-new-in-5.md b/docs/getting-started/what-is-new-in-5.md deleted file mode 100644 index 78c51776..00000000 --- a/docs/getting-started/what-is-new-in-5.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: What's New in 5.0 -description: The highlights of the RomM 5.0 release: features, changes, and what it means for you. ---- - -# What's New in 5.0 - -RomM 5.0 is the biggest release since the project started. Here's what's landed, organised by who it's for. - -!!! warning "Upgrading from 4.x?" - Read the [**Upgrading to 5.0**](../releases/upgrading-to-5.0.md) guide before you pull the new image. There are breaking changes: migrations, env var renames, and config shifts. - -## For end users - -### Console Mode - -A brand-new `/console` UI designed for TVs and gamepads. Spatial navigation, focus sounds, large hit targets, gamepad-first controls. Built as a separate SPA sharing the same data as the main UI, with no extra deploy: just open `/console`. [Read more →](../using/console-mode.md) - -### Smart & Virtual Collections - -**Standard** collections (curated by you) now join two new flavours: - -- **Smart Collections**: rule-based, auto-populating. Define a query, RomM keeps it in sync. -- **Virtual Collections**: auto-generated by RomM by genre, developer, year, or tag. - -[Collections → ](../using/collections.md) - -### ROM Patcher - -Apply IPS / UPS / BPS / PPF / RUP / APS / BDF / PMSR / vcdiff patches in the browser. Save the result locally or push it back into the library. [ROM Patcher →](../using/rom-patcher.md) - -### Netplay - -Multiplayer for EmulatorJS sessions: host or join rooms, with ICE server configuration for NAT traversal. [Netplay →](../using/netplay.md) - -### PWA install - -RomM ships with a proper manifest and service worker. Install to your home screen on iOS / Android / desktop for an app-like experience. [Install as PWA →](../using/pwa.md) - -### 19 locales - -English (US/GB), Spanish, French, German, Italian, Portuguese (BR), Japanese, Korean, Russian, Polish, Czech, Hungarian, Romanian, Bulgarian, Chinese (Simplified/Traditional). Switch in User Interface settings. [Languages →](../using/languages.md) - -### Time to Beat & RetroAchievements tabs - -HowLongToBeat completion times and RetroAchievements progression now surface as dedicated tabs on every game's detail page. Both need provider credentials. See [Metadata Providers](../administration/metadata-providers.md). - -### Bulk downloads, QR codes, copy-link - -Every ROM gets a one-click Copy Link button and a QR code for mobile sharing. Bulk downloads stream multi-ROM zips via nginx's `mod_zip`. - -## For operators - -### Upgrade-grade migration - -4.x → 5.0 is a schema-migrating upgrade. Alembic runs on startup and takes care of the heavy lifting, but you should [back up first](../install/backup-and-restore.md). Full migration playbook in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). - -### Thirteen metadata providers - -Up from nine. New in 5.0: **TheGamesDB**, **Libretro metadata**, **gamelist.xml importer** (for ES-DE / Batocera migrations). [Metadata Providers →](../administration/metadata-providers.md) - -### Slim + Full images - -Two official image variants: - -- **Full** (default): includes EmulatorJS and Ruffle. ~1.2 GB. -- **Slim**: no in-browser emulators. ~400 MB. - -Pick based on whether you want in-browser play. [Image Variants →](../install/image-variants.md) - -### Expanded OIDC - -- **Role mapping** from OIDC claims to RomM roles (`OIDC_CLAIM_ROLES`, `OIDC_ROLE_VIEWER/EDITOR/ADMIN`). -- **RP-initiated logout** support. -- **Autologin** (skip the RomM login page and bounce straight to the IdP). -- Keycloak is now a first-class guide alongside Authelia, Authentik, PocketID, Zitadel. - -[OIDC Setup →](../administration/oidc/index.md) - -### Invite links + public registration - -First-class invite flow (`INVITE_TOKEN_DAYS`). Opt-in public self-registration (`ALLOW_PUBLIC_REGISTRATION`) for instances already behind upstream auth. [Invitations & Registration →](../administration/invitations-and-registration.md) - -### Real-time filesystem watcher - -Toggle-able `WATCHER_ENABLED` with debounce, extension filtering, and intelligent batching. Pairs well with the scheduled scan. [Scanning & Watcher →](../administration/scanning-and-watcher.md) - -### Observability - -- **OpenTelemetry** export via `OTEL_ENABLED`. -- **Sentry** integration (opt-in). -- Per-task status in the admin UI and via `/api/heartbeat`. - -[Observability →](../administration/observability.md) - -### Kubernetes-ready - -First-class K8s manifest examples, including the mandatory `enableServiceLinks: false` gotcha. No official Helm chart yet, but the community charts on ArtifactHub are reasonable. [Kubernetes →](../install/kubernetes.md) - -## For ecosystem developers - -### Client API Tokens + device pairing - -Per-user bearer tokens (max 25) with a subset of scopes, optionally paired to a device via a short code, so your handheld app doesn't have to type a 40-character secret. [Client API Tokens →](../ecosystem/client-api-tokens.md) - -### Device Sync protocol - -New endpoints under `/api/devices/`, `/api/sync/`, and `/api/play-sessions/` for bidirectional save/state sync and play-session ingestion. Grout, Argosy, and DeckRommSync are early consumers. [Device Sync Protocol →](../ecosystem/device-sync-protocol.md) - -### Expanded feed endpoints - -Existing WebRcade and Tinfoil feeds now joined by PKGi (PS3/Vita/PSP), FPKGi (PS4/PS5), Kekatsu (DS), and PKGj formats. [Feeds reference →](../reference/feeds.md) - -### Gamelist.xml + Pegasus export - -Export your RomM library to ES-DE / Batocera gamelist.xml or Pegasus metadata format, for users bouncing between frontends. - -### WebSockets - -Two endpoints: `/ws/socket.io` for general live updates (scan progress, notifications) and `/netplay/socket.io` for netplay session coordination. Redis-backed pubsub for multi-instance deployments. [WebSockets →](../developers/websockets.md) - -## Breaking changes at a glance - -| Area | What changed | -| --- | --- | -| **Database schema** | Alembic migrates automatically, but back up first. | -| **Env var names** | A few renames around scheduled-task cron vars and OIDC role mapping. See [migration table](../releases/upgrading-to-5.0.md). | -| **`config.yml`** | New sections for `scan.region`, `scan.language`, `scan.media`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings carry over. | -| **URL structure** | Docs URLs restructured (you're reading the new ones). Every old URL redirects. See [the redirect map](../releases/upgrading-to-5.0.md) in the migration guide. | -| **Image tags** | `:slim` and `:5.0.0-slim` are new options alongside `:latest` and `:5.0.0`. | - -## Where to go from here - -- **Upgrading**: [Upgrading to 5.0](../releases/upgrading-to-5.0.md) -- **Fresh install**: [Quick Start](quick-start.md) -- **What does X mean?**: [Core Concepts](concepts.md) -- **Full changelog**: [Changelog](../releases/changelog.md) From 120383b10934adc6aec330700185b1e82bc992f7 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 10:10:37 -0400 Subject: [PATCH 035/121] start cleanup of install --- docs/install/docker-compose.md | 4 +--- docs/install/index.md | 34 ++++++++++++---------------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index 2f574637..68e845a8 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -9,9 +9,8 @@ The canonical way to run RomM is with Docker Compose. This page describes the fu The RomM stack has three parts: -1. **`romm`**: the application container (FastAPI backend, Vue frontend, nginx, and an embedded Redis/Valkey worker). +1. **`romm`**: the application container with Valkey embedded. 2. **A database**: MariaDB by default, but MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. -3. **Redis or Valkey**: required for background tasks, session storage, and queue coordination. In the default `full-image` this runs inside the RomM container, but for production you'll typically split it out. See [Redis or Valkey](redis-or-valkey.md). ## Reference `docker-compose.yml` @@ -69,7 +68,6 @@ The quick-start compose file is functional but not production-ready. Before expo - **Pin image tags.** `rommapp/romm:latest` moves, so use `rommapp/romm:5.0.0` (or whatever release you're on). - **Use a reverse proxy with HTTPS.** The built-in nginx listens on `8080` and terminates plain HTTP. Put Traefik, Caddy, or nginx in front with TLS. See [Reverse Proxy](reverse-proxy.md). -- **Split out Redis/Valkey.** The `full-image` embeds Redis, so for production run a dedicated container and set `REDIS_HOST`. See [Redis or Valkey](redis-or-valkey.md). - **Set a non-default `MARIADB_ROOT_PASSWORD`.** And don't reuse it for `MARIADB_PASSWORD`. - **Mount the library read-only** unless you need RomM to write into it: `- /path/to/library:/romm/library:ro`. - **Use Docker secrets for credentials** if your orchestrator supports them. RomM recognises `ROMM_AUTH_SECRET_KEY_FILE` for loading the secret from a file mount. diff --git a/docs/install/index.md b/docs/install/index.md index 5a6a1357..32d92d5f 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -1,6 +1,6 @@ --- title: Install & Deploy -description: Pick a deployment path: Docker Compose, Unraid, Synology, TrueNAS, or Kubernetes. +description: On Docker Compose, Unraid, Synology, TrueNAS, or Kubernetes. --- # Install & Deploy @@ -11,11 +11,11 @@ RomM is distributed as a Docker image. Every supported deployment runs the same | If you're on… | Start here | | --- | --- | -| A Linux server / NAS with Docker | **[Docker Compose](docker-compose.md)**: the canonical reference setup. | -| **Unraid** | [Unraid](unraid.md): two supported paths (Community Apps template, Docker Compose Manager). | -| **Synology** | [Synology](synology.md): Container Manager + DSM-specific notes. | -| **TrueNAS SCALE** | [TrueNAS](truenas.md): App Catalog or YAML install. | -| **Kubernetes** | [Kubernetes](kubernetes.md): manifest examples, required quirks. | +| **Linux server / NAS** | [The canonical reference setup](docker-compose.md) | +| **Unraid** | [Community Apps template or DCM](unraid.md) | +| **Synology** |[Container Manager + DSM-specific notes](synology.md) | +| **TrueNAS** | [App Catalog or YAML install](truenas.md) | +| **Kubernetes** | [Manifest examples](kubernetes.md) | If none of those match, start with [Docker Compose](docker-compose.md) and adapt. @@ -23,24 +23,14 @@ If none of those match, start with [Docker Compose](docker-compose.md) and adapt Regardless of host platform, you'll make the same handful of decisions: -- **[Image variant](image-variants.md)**: `full` (default, includes in-browser emulators) or `slim` (headless, smaller footprint). -- **[Database](databases.md)**: MariaDB (default), MySQL, PostgreSQL, or SQLite (dev-only). -- **[Redis / Valkey](redis-or-valkey.md)**: required for sessions + task queue. Embedded or external. -- **[Reverse proxy](reverse-proxy.md)**: Caddy, nginx, Traefik, or NPM. HTTPS required for OIDC and PWA install. -- **[Backup & restore](backup-and-restore.md)**: don't skip. Test the restore before you need it. - -## Minimum reading before going live - -1. [Quick Start](../getting-started/quick-start.md): 15-minute happy-path walkthrough. -2. [Docker Compose](docker-compose.md): production-oriented reference compose. -3. [Reverse Proxy](reverse-proxy.md): pick a TLS-terminating proxy. -4. [Backup & Restore](backup-and-restore.md): nightly backup + restore drill. - -Then come back here when you're ready to pick a platform-specific guide, or read [Administration](../administration/index.md) to set up your first users, metadata, and scheduled tasks. +- **[Image variant](image-variants.md)**: `full` (default, includes in-browser emulators) or `slim` (headless, smaller memory footprint). +- **[Database](databases.md)**: MariaDB (default), MySQL or PostgreSQL. +- **[In-memory store](redis-or-valkey.md)**: required for sessions + task queue, embedded or external. +- **[Reverse proxy](reverse-proxy.md)**: Caddy, nginx, Traefik, or NPM. HTTPS is required for OIDC and PWA install. +- **[Backup & restore](backup-and-restore.md)**: Test it before you need it! ## After you're up and running +- **Configure metadata providers**: [Metadata Providers](../administration/metadata-providers.md). - **Populate the library**: [Your First Scan](../getting-started/first-scan.md). - **Add users**: [Invitations & Registration](../administration/invitations-and-registration.md). -- **Configure metadata providers**: [Metadata Providers](../administration/metadata-providers.md). -- **Upgrading from 4.x?** [Upgrading to 5.0](../releases/upgrading-to-5.0.md). From 640f2a925e9a488d7fd2b32ddc3fea1a6f6ce7eb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 14:23:50 +0000 Subject: [PATCH 036/121] docs: smooth staccato prose into flowing sentences Combine clipped fragments into connected sentences (via commas, colons, parentheticals) while keeping them short. Touches ~20 files across reference, admin, using, install, getting-started, ecosystem, platforms, troubleshooting, and landing pages. https://claude.ai/code/session_0121DXEECS43gKkdvtUuuPAd --- docs/about/faqs.md | 10 ++--- docs/about/license.md | 8 ++-- docs/administration/authentication.md | 12 +++--- docs/administration/observability.md | 4 +- docs/administration/scanning-and-watcher.md | 28 +++++++------- docs/administration/users-and-roles.md | 14 +++---- docs/ecosystem/argosy.md | 26 ++++++------- docs/ecosystem/index.md | 6 +-- docs/getting-started/first-scan.md | 12 +++--- docs/index.md | 4 +- docs/install/docker-compose.md | 34 ++++++++--------- docs/platforms/ms-dos.md | 42 ++++++++++----------- docs/reference/environment-variables.md | 10 ++--- docs/releases/upgrading-to-5.0.md | 14 +++---- docs/troubleshooting/authentication.md | 30 +++++++-------- docs/troubleshooting/scanning.md | 38 +++++++++---------- docs/using/account-and-profile.md | 18 ++++----- docs/using/downloads.md | 12 +++--- docs/using/library.md | 6 +-- 19 files changed, 164 insertions(+), 164 deletions(-) diff --git a/docs/about/faqs.md b/docs/about/faqs.md index 09d1c39c..008efd8f 100644 --- a/docs/about/faqs.md +++ b/docs/about/faqs.md @@ -7,17 +7,17 @@ description: Answers to the questions users most often ask about RomM. ## What is RomM? -A self-hosted ROM manager + player. Scan your library, get metadata, browse a clean UI, play in the browser, sync to handhelds, run it on your own hardware. +A self-hosted ROM manager + player: scan your library, pull metadata, browse a clean UI, play in the browser, sync to handhelds, and run it all on your own hardware. See [Introduction](../index.md) for the full pitch. ## Is it free? -Yes. [AGPL-3.0](license.md). Core always will be free, and other repos in the umbrella use permissive licenses. No tracking, no upsells. +Yes, under [AGPL-3.0](license.md). The core will always be free, other repos in the umbrella use permissive licenses, and there's no tracking or upsells. ## How does it compare to [X other manager]? -Not a direct comparison page. Short version: RomM emphasises self-hosted + multi-user + in-browser-play + companion-app ecosystem. If those matter, try RomM. If you just want a local Windows app that scans a folder, tools like LaunchBox may fit better. +Not a direct comparison page, but the short version: RomM emphasises self-hosted + multi-user + in-browser-play + companion-app ecosystem. If those matter, try RomM; if you just want a local Windows app that scans a folder, tools like LaunchBox may fit better. ## Do I need metadata API keys? @@ -27,7 +27,7 @@ Recommended: IGDB + ScreenScraper. See [Metadata Providers](../administration/me ## Is RomM legal? -The software is legal. What you put in it depends on your jurisdiction. We don't ship ROMs or firmware, don't help you find them, and can't give legal advice. General rule: dumping games you own is usually fine, distributing copies is usually not. +The software is legal, but what you put in it depends on your jurisdiction. We don't ship ROMs or firmware, don't help you find them, and can't give legal advice. General rule: dumping games you own is usually fine, distributing copies is usually not. ## Can I run RomM on [X]? @@ -116,7 +116,7 @@ For public roadmap-ish information, watch the GitHub Projects board and `#announ ## What happened to SQLite? -Dropped in 3.0 for stability reasons. Use MariaDB, MySQL, or Postgres instead. See [Databases](../install/databases.md). +Dropped in 3.0 for stability reasons: use MariaDB, MySQL, or Postgres instead. See [Databases](../install/databases.md). ## I found a bug. diff --git a/docs/about/license.md b/docs/about/license.md index db25655d..7faeb825 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -53,19 +53,19 @@ Yes. AGPL doesn't restrict private or commercial use. ### Can I fork RomM and relicense my fork? -No. AGPL is a strong copyleft, so forks remain AGPL. You can add your own changes under AGPL, but can't relicense the original code. +No: AGPL is a strong copyleft, so forks remain AGPL, and while you can add your own changes under AGPL you can't relicense the original code. ### Can I charge money for RomM-as-a-service? -Yes, but you owe your users the source. You can't run a modified closed-source hosted RomM for paying customers. +Yes, but you owe your users the source, so you can't run a modified closed-source hosted RomM for paying customers. ### Can I sell ROMs through RomM? -RomM doesn't care. Legality of doing so is governed by copyright law, not AGPL. +RomM doesn't care: legality of doing so is governed by copyright law, not AGPL. ### Is there a commercial / dual-license option? -No. AGPL-only. If that's a blocker, RomM isn't the right choice. +No, AGPL-only, and if that's a blocker then RomM isn't the right choice. ## Contributions diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index d715b383..af9e8a18 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -12,8 +12,8 @@ Authentication flows RomM supports: - **Username + password** (default): local account, bcrypt-hashed, stored in the DB. - **OIDC**: single sign-on via an external IdP. See [OIDC Setup](oidc/index.md). - **Client API Tokens**: long-lived per-user tokens for companion apps and scripts. -- **Device pairing**: short codes for bootstrapping a token onto a handheld. Covered in [Client API Tokens](../ecosystem/client-api-tokens.md). -- **Kiosk mode**: unauthenticated read-only access. Toggle for public demos / shared terminals. +- **Device pairing**: short codes for bootstrapping a token onto a handheld, covered in [Client API Tokens](../ecosystem/client-api-tokens.md). +- **Kiosk mode**: unauthenticated read-only access, handy for public demos and shared terminals. ## Session config @@ -30,7 +30,7 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: ## Local (username + password) -The default. Accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md). Passwords are bcrypt-hashed, and RomM does not log or store plaintext passwords at any point. +The default: accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md), and passwords are bcrypt-hashed. RomM does not log or store plaintext passwords at any point. **Disable local password login entirely** (force OIDC-only): @@ -48,7 +48,7 @@ Until email-based self-serve reset lands, admins set passwords manually: **Administration → Users → Edit → New password → Save.** -The next login on that account will use the new password. Existing sessions for that user remain valid until they expire. Revoke them explicitly by deleting all of the user's Client API Tokens if that's a concern. +The next login on that account will use the new password, but existing sessions for that user remain valid until they expire. If that's a concern, revoke them explicitly by deleting all of the user's Client API Tokens. ### Self-serve password reset @@ -81,7 +81,7 @@ Create from **Administration → Client API Tokens**. Each token: - Has an optional expiry (no expiry = never expires until manually revoked). - Can be "paired" to a device via a short code (covered in [Client API Tokens](../ecosystem/client-api-tokens.md)). -Each user gets up to 25 active tokens. Revoke from the same page. The API side ("how do I send this thing in a request?") lives in [API Authentication](../developers/api-authentication.md). +Each user gets up to 25 active tokens, revokable from the same page. The API side ("how do I send this thing in a request?") lives in [API Authentication](../developers/api-authentication.md). ## Kiosk mode @@ -119,4 +119,4 @@ To fully cut a user off: 2. Delete all their Client API Tokens (**Administration → Client API Tokens**, filter by user). 3. Delete the user. -Steps 1 + 2 together ensure any in-flight session and any token-based companion app lose access immediately. Step 3 removes the account. +Steps 1 + 2 together ensure any in-flight session and any token-based companion app lose access immediately, and step 3 removes the account. diff --git a/docs/administration/observability.md b/docs/administration/observability.md index e9f79de5..0a88761e 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -85,7 +85,7 @@ Useful when a scan is matching poorly and you want to know whether a provider is ## Sentry -Opt-in error tracking. Nothing is sent without a DSN. +Opt-in error tracking: nothing is sent without a DSN. ```yaml environment: @@ -100,7 +100,7 @@ What's sent: What's not sent: ROM filenames, user credentials, metadata provider API keys. RomM filters sensitive parameters before reporting. -Suitable for self-hosted Sentry or [sentry.io](https://sentry.io/). Drop the DSN to stop reporting. +Suitable for self-hosted Sentry or [sentry.io](https://sentry.io/); drop the DSN to stop reporting. ## OpenTelemetry diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 811a93a5..205452dd 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -19,12 +19,12 @@ Every scan picks one mode. Modes differ in what they touch, so use the most-targ | Mode | What it does | When to use | | --- | --- | --- | -| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set. Fast. | -| **Quick** | Skips files that already exist in the DB. No metadata refresh. | Default for scheduled runs and the watcher. | +| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set; it's fast. | +| **Quick** | Skips files that already exist in the DB, with no metadata refresh. | Default for scheduled runs and the watcher. | | **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | -| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured). Rare. | +| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured); rare. | | **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash (pre-4.4) or when you suspect file corruption. | -| **Complete** | Full rescan. Recalculates hashes, re-fetches metadata for everything. | Rarely. Takes a long time. | +| **Complete** | Full rescan, recalculating hashes and re-fetching metadata for everything. | Rarely, since it takes a long time. | You can further scope a scan to specific **platforms** and specific **metadata providers**, useful when only one provider has changed (e.g. just enabled Hasheous → Unmatched scan, Hasheous selected, on all platforms). @@ -47,15 +47,15 @@ Configured via env vars (full table in the [Scheduled Tasks reference](../refere | Variable | Default | Purpose | | --- | --- | --- | | `SCAN_INTERVAL_CRON` | `0 0 * * *` | Cron expression for the scheduled library scan. Runs a **Quick** scan by default. | -| `SCAN_TIMEOUT_HOURS` | `1` | Hard cap. Scans that exceed this are killed and logged. | -| `SCAN_WORKERS` | _auto_ | Concurrent worker processes for scanning. Leave as auto unless you're tuning. | -| `SEVEN_ZIP_TIMEOUT` | _unset_ | Per-archive timeout for `.7z` extraction during scan. Raise if scanning huge compressed ROM sets. | +| `SCAN_TIMEOUT_HOURS` | `1` | Hard cap: scans that exceed this are killed and logged. | +| `SCAN_WORKERS` | _auto_ | Concurrent worker processes for scanning; leave as auto unless you're tuning. | +| `SEVEN_ZIP_TIMEOUT` | _unset_ | Per-archive timeout for `.7z` extraction during scan; raise if scanning huge compressed ROM sets. | To disable scheduled scans entirely, either unset the cron or set it to something unreachable (`SCAN_INTERVAL_CRON=0 0 31 2 *`). ## Filesystem watcher -The watcher tails your library folder and schedules scans in response to file events: files added, moved, deleted. It's off by default on some deployments. Enable with: +The watcher tails your library folder and schedules scans in response to file events (files added, moved, or deleted). It's off by default on some deployments, so enable with: ```yaml environment: @@ -68,16 +68,16 @@ environment: Behaviour: - Watches `/romm/library` (and everything under it) recursively. -- Debounces bursts of events. The delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. +- Debounces bursts of events: the delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. - Batches scans intelligently: many events → a single consolidated scan, not one scan per file. -- Ignores content modifications and metadata-only changes. It cares about files appearing or disappearing, not about `chmod`. +- Ignores content modifications and metadata-only changes, caring only about files appearing or disappearing (not `chmod`). - Skips OS noise (`.DS_Store`, `Thumbs.db`, `.tmp`, etc.). - If a whole new platform folder appears, switches to a **New Platforms** scan to pick it up cleanly. ### When **not** to enable the watcher -- **Slow / high-latency filesystems** (SMB mounts, rclone mounts, anything not local disk). The watcher reacts to every event, and flaky mounts generate a lot. Use scheduled scans instead. -- **Libraries under active write load from other tools** (e.g. a ROM manager constantly tagging files). The watcher will re-scan on every change: at best noisy, at worst a scan loop. +- **Slow / high-latency filesystems** (SMB mounts, rclone mounts, anything not local disk): the watcher reacts to every event, flaky mounts generate a lot of them, so use scheduled scans instead. +- **Libraries under active write load from other tools** (e.g. a ROM manager constantly tagging files): the watcher will re-scan on every change, at best noisy and at worst a scan loop. ### Watcher vs scheduled scan @@ -89,7 +89,7 @@ Behaviour: | Catches renames | Yes | Yes | | Survives a container restart | Yes, re-arms on startup | Yes | -Run both. The watcher handles day-to-day additions, and the scheduled scan is a safety net. +Run both: the watcher handles day-to-day additions, and the scheduled scan is a safety net. ## What gets excluded @@ -127,7 +127,7 @@ When a metadata provider returns multiple regional variants (Japanese cover, US ## Metadata source priority -Who wins when two providers disagree: covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution). Short version: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. +Who wins when two providers disagree is covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution); short version: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. ## Troubleshooting diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index 6ef8197d..cd42778f 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -15,7 +15,7 @@ RomM is multi-user from the start. The first user created during Setup is always | **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | | **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | -Roles are a convenience layer on top of **scopes**. See the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0. If you need finer-grained access, use the role that's most restrictive and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. +Roles are a convenience layer on top of **scopes**; see the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0, so if you need finer-grained access, use the most restrictive role and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. ## Scope matrix @@ -49,27 +49,27 @@ Two ways: ### Admin adds directly -**Administration → Users → Add.** Set username, email, password, and role. The account is usable immediately. +**Administration → Users → Add.** Set username, email, password, and role, and the account is usable immediately. ### Invite link Better when you don't want to handle someone else's password. 1. **Administration → Users → Invite.** Pick a role, and RomM generates a single-use invite link. -2. Send the link. The recipient opens it, picks their own username and password, and is logged in. -3. Invite links expire. The default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). +2. Send the link, and the recipient opens it, picks their own username and password, and is logged in. +3. Invite links expire: the default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). ### Public self-registration -Off by default. To let anyone with the URL register their own Viewer account, set `ALLOW_PUBLIC_REGISTRATION=true`. Only enable this if your instance is behind auth at the reverse-proxy layer (Authelia, etc.) or you genuinely want open registration. Once on, anyone who reaches `/register` can create an account. +Off by default. To let anyone with the URL register their own Viewer account, set `ALLOW_PUBLIC_REGISTRATION=true`. Only enable this if your instance is behind auth at the reverse-proxy layer (Authelia, etc.) or you genuinely want open registration: once on, anyone who reaches `/register` can create an account. ### OIDC -If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md). Look for `OIDC_CLAIM_ROLES` and the per-role env vars. +If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md): look for `OIDC_CLAIM_ROLES` and the per-role env vars. ## Editing and deleting users -- **Change role**: Admin → Users → Edit → Role dropdown. Takes effect on next login. +- **Change role**: Admin → Users → Edit → Role dropdown, taking effect on next login. - **Reset password**: Admin → Users → Edit → New password. For self-service, the user can use the "Forgot password" flow from the login page if email is configured. - **Delete**: Admin → Users → red delete icon → confirm. RomM won't let you delete the last admin or delete yourself while signed in. diff --git a/docs/ecosystem/argosy.md b/docs/ecosystem/argosy.md index 607a4216..6be2b359 100644 --- a/docs/ecosystem/argosy.md +++ b/docs/ecosystem/argosy.md @@ -5,7 +5,7 @@ description: Official Android launcher for RomM, browse and launch your library # Argosy Launcher -**Argosy Launcher** is RomM's first-party Android app. Browse your library, download ROMs on the fly, launch into RetroArch or your emulator of choice. +**Argosy Launcher** is RomM's first-party Android app: browse your library, download ROMs on the fly, and launch into RetroArch or your emulator of choice. - **Repo:** [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher) - **Language:** Kotlin @@ -29,16 +29,16 @@ description: Official Android launcher for RomM, browse and launch your library ### Via F-Droid / Play Store -Not currently on either. APK sideloading is the path for now. +Not currently on either, so APK sideloading is the path for now. ## First-time setup 1. Launch Argosy. -2. Enter your RomM URL (e.g. `https://romm.example.com`). Needs HTTPS in production. +2. Enter your RomM URL (e.g. `https://romm.example.com`), which needs HTTPS in production. 3. Choose **Pair Device**. 4. Argosy shows a pairing URL or QR code: open it on a device that's already signed into RomM. -5. On that device: RomM shows a confirmation dialog. Enter the 8-digit code Argosy displayed. -6. Accept. Argosy receives a Client API Token bound to your RomM account. +5. On that device, RomM shows a confirmation dialog; enter the 8-digit code Argosy displayed. +6. Accept, and Argosy receives a Client API Token bound to your RomM account. From here, Argosy lists your library. Full pairing-flow details in [Client API Tokens](client-api-tokens.md). @@ -46,11 +46,11 @@ From here, Argosy lists your library. Full pairing-flow details in [Client API T ### Browse -The main view mirrors RomM's platform-and-collection layout. Tap a platform to see its games. Search works the same as the RomM web UI. +The main view mirrors RomM's platform-and-collection layout: tap a platform to see its games, and search works the same as the RomM web UI. ### Download and launch -Tap a game → **Download**. Argosy pulls the ROM to device storage. Once downloaded, **Play** launches it with your configured emulator. +Tap a game → **Download** and Argosy pulls the ROM to device storage. Once downloaded, **Play** launches it with your configured emulator. ### Emulator configuration @@ -68,7 +68,7 @@ Argosy → Settings → Sync. Two modes: - **On session end**: uploads saves back to RomM when you exit the emulator. - **Manual**: you tap Upload when you want. -Saves show up in RomM's **Game Data** tab on the game, same as in-browser saves. See [Saves & States](../using/saves-and-states.md). +Saves show up in RomM's **Game Data** tab on the game, same as in-browser saves: see [Saves & States](../using/saves-and-states.md). ## Permissions @@ -78,14 +78,14 @@ Argosy needs: - **Network**: to talk to your RomM instance. - **Optional: Notifications**: download-complete and sync-complete pings. -No other permissions. The app doesn't request contacts, camera, location, or anything else. +No other permissions: the app doesn't request contacts, camera, location, or anything else. ## Troubleshooting -- **Can't connect to RomM.** Check the URL (including `https://`), and that the RomM instance is reachable from your mobile network. Cellular might be blocked, so try Wi-Fi first. -- **Token invalid.** Pair again. Tokens can expire or be revoked on the RomM side. -- **Emulator won't launch.** Make sure the emulator app is installed and Argosy has permission to open it. Some emulators require an intent-filter setup. -- **Downloads fail partway.** Usually network, and Argosy resumes on retry. +- **Can't connect to RomM**: check the URL (including `https://`) and that the RomM instance is reachable from your mobile network. Cellular might be blocked, so try Wi-Fi first. +- **Token invalid**: pair again, since tokens can expire or be revoked on the RomM side. +- **Emulator won't launch**: make sure the emulator app is installed and Argosy has permission to open it; some emulators require an intent-filter setup. +- **Downloads fail partway**: usually network, and Argosy resumes on retry. Full sync-specific debugging in [Device Sync Troubleshooting](../troubleshooting/sync.md). diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md index 364a2113..d7331bb8 100644 --- a/docs/ecosystem/index.md +++ b/docs/ecosystem/index.md @@ -30,7 +30,7 @@ See the [full feeds reference](../reference/feeds.md) for URL formats, auth requ ## Community apps -Maintained by individuals in the community, not the RomM team. Support quality varies. +Maintained by individuals in the community, not the RomM team, so support quality varies. See **[Community Apps](community-apps.md)** for the full list with status flags (active / maintenance-mode / abandoned) and links. @@ -62,10 +62,10 @@ For developers building something new on top of RomM: Not a RomM companion, but useful alongside: -- **[Igir Collection Manager](igir.md)**: ROM sorting/verifying tool. Cleans up library layout before importing into RomM. +- **[Igir Collection Manager](igir.md)**: ROM sorting/verifying tool that cleans up library layout before importing into RomM. ## Contributing a companion app Built something RomM-adjacent? Open a PR on [rommapp/docs](https://github.com/rommapp/docs) adding it to [Community Apps](community-apps.md), or drop a link in the [Discord](https://discord.gg/romm) `#community-projects` channel. -We list active, maintained projects. No gate on code quality, but we do flag abandoned projects so users know what's current. +We list active, maintained projects, with no gate on code quality, but we do flag abandoned projects so users know what's current. diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md index 97ca8599..ec4b3dd2 100644 --- a/docs/getting-started/first-scan.md +++ b/docs/getting-started/first-scan.md @@ -27,7 +27,7 @@ The page switches to a live feed of the scan's progress. You can leave and come - Per-platform **accordion panels** show counts update live: total found, matched, unmatched. - You can click a **matched ROM** while the scan is still running to see what metadata RomM pulled, with no need to wait for the full run. -First scans on big libraries take a while. Expect ~4 seconds per ROM with a fast network to IGDB/ScreenScraper, and hashing (which runs unless you disabled it) adds IO time proportional to file size. +First scans on big libraries take a while: expect ~4 seconds per ROM with a fast network to IGDB/ScreenScraper, and hashing (which runs unless you disabled it) adds IO time proportional to file size. ## What "matched" means @@ -38,14 +38,14 @@ For each ROM the scanner: 3. **Writes the DB entry** with title, cover, description, release date, and anything else the winning provider returned. 4. **Merges overlay data** from other providers: RetroAchievements progression, HowLongToBeat completion times, SteamGridDB alternate covers if you've asked. -An **unmatched** ROM means no provider recognised it. Common causes: +An **unmatched** ROM means no provider recognised it, with common causes: - Filename is too generic (`game.gba`). - Bad rip, intro/patch applied, or a regional variant no provider has indexed. - Platform folder misnamed: the scanner queries providers scoped to the detected platform, so wrong platform equals no results. - Metadata provider credentials wrong or rate-limited: check the scan log for errors or the metadata provider status page in the admin dashboard. -Most of these are fixable. See [Scanning Troubleshooting](../troubleshooting/scanning.md). +Most of these are fixable: see [Scanning Troubleshooting](../troubleshooting/scanning.md). ## When the scan finishes @@ -66,9 +66,9 @@ From here, typical next steps: If you're adding ROMs later and don't want a full rescan: -- **New Platforms**: only scans folders RomM hasn't seen before. Fast. -- **Quick**: skips ROMs already catalogued. Good default for "I added a few games". -- **Unmatched**: re-runs matching against ROMs without a provider ID. Good after adding a metadata provider. +- **New Platforms**: only scans folders RomM hasn't seen before, and it's fast. +- **Quick**: skips ROMs already catalogued, a good default for "I added a few games". +- **Unmatched**: re-runs matching against ROMs without a provider ID, ideal after adding a metadata provider. All six scan modes are documented in [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). diff --git a/docs/index.md b/docs/index.md index a0cecee0..08ceae15 100644 --- a/docs/index.md +++ b/docs/index.md @@ -75,7 +75,7 @@ RomM (ROM Manager) lets you scan, enrich, organise, and play your game collectio ## Philosophy -RomM is built for its users, not for shareholders. Self-hosted, open-source, no tracking, no upsells. The core app is licensed under [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/). Other projects in the umbrella use permissive licenses ([GPLv3](https://choosealicense.com/licenses/gpl-3.0/) for software, [CC0](https://choosealicense.com/licenses/cc0-1.0/) for documentation). +RomM is built for its users, not for shareholders: self-hosted, open-source, no tracking, no upsells. The core app is licensed under [GNU AGPLv3](https://choosealicense.com/licenses/agpl-3.0/), and other projects in the umbrella use permissive licenses ([GPLv3](https://choosealicense.com/licenses/gpl-3.0/) for software, [CC0](https://choosealicense.com/licenses/cc0-1.0/) for documentation). **RomM is and will always be free and open-source software.** @@ -87,4 +87,4 @@ RomM is built for its users, not for shareholders. Self-hosted, open-source, no -Join us on Discord to ask questions, share your setup, request features, or help other users. Code, issues, and releases live on [GitHub](https://github.com/rommapp/romm). +Join us on Discord to ask questions, share your setup, request features, or help other users; code, issues, and releases live on [GitHub](https://github.com/rommapp/romm). diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index 68e845a8..f8e9f3f3 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -5,7 +5,7 @@ description: Canonical Docker Compose reference for a production RomM 5.0 deploy # Docker Compose -The canonical way to run RomM is with Docker Compose. This page describes the full reference stack. The shorter happy-path walkthrough lives in [Quick Start](../getting-started/quick-start.md). +The canonical way to run RomM is with Docker Compose, and this page describes the full reference stack. The shorter happy-path walkthrough lives in [Quick Start](../getting-started/quick-start.md). The RomM stack has three parts: @@ -24,8 +24,8 @@ The RomM stack has three parts: | Field | Value | Notes | | --- | --- | --- | -| `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`. Pin to a specific tag (`5.0.0`) for production. See [Image Variants](image-variants.md) for `slim` vs `full`. | -| `ports` | `80:8080` | Container listens on `8080`. Expose through a reverse proxy in production. See [Reverse Proxy](reverse-proxy.md). | +| `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`; pin to a specific tag (`5.0.0`) for production, and see [Image Variants](image-variants.md) for `slim` vs `full`. | +| `ports` | `80:8080` | Container listens on `8080`; expose through a reverse proxy in production (see [Reverse Proxy](reverse-proxy.md)). | | `volumes` | see below | RomM writes to four distinct paths inside the container. | | `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | @@ -33,22 +33,22 @@ The RomM stack has three parts: | Path inside container | Purpose | Backup priority | | --- | --- | --- | -| `/romm/library` | Your ROM files. Typically mounted **read-only**. | No. This is your source data, back it up separately. | -| `/romm/assets` | User uploads: saves, states, uploaded screenshots. | **Critical.** Back this up with your DB. | +| `/romm/library` | Your ROM files, typically mounted **read-only**. | No: this is your source data, back it up separately. | +| `/romm/assets` | User uploads: saves, states, uploaded screenshots. | **Critical**, back this up with your DB. | | `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low, and can be re-downloaded on a rescan. | -| `/romm/config` | Holds `config.yml`. | **Critical.** Hand-tuned config, back it up. | +| `/romm/config` | Holds `config.yml`. | **Critical**, hand-tuned config, back it up. | See [Backup & Restore](backup-and-restore.md) for the full procedure. #### Core environment variables -Minimal set. See the [Environment Variables reference](../reference/environment-variables.md) for the complete list. +Minimal set: see the [Environment Variables reference](../reference/environment-variables.md) for the complete list. | Variable | What it does | | --- | --- | -| `ROMM_AUTH_SECRET_KEY` | JWT signing secret. **Required.** Generate with `openssl rand -hex 32`. | +| `ROMM_AUTH_SECRET_KEY` | (Required) Generate the JWT signing secret with `openssl rand -hex 32`. | | `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | -| `ROMM_DB_DRIVER` | `mariadb` (default), `mysql`, or `postgresql`. | +| `ROMM_DB_DRIVER` | One of `mariadb` (default), `mysql`, or `postgresql`. | | `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWD` | Only set if you're using an external Redis/Valkey instance. | | Metadata provider creds | See [Metadata Providers](../administration/metadata-providers.md). | @@ -57,21 +57,21 @@ Minimal set. See the [Environment Variables reference](../reference/environment- | Field | Value | Notes | | --- | --- | --- | | `image` | `mariadb:latest` | Pin to a major version (`mariadb:11`) for production. | -| `volumes` | `mysql_data:/var/lib/mysql` | Back this up. It's your entire catalogue. | +| `volumes` | `mysql_data:/var/lib/mysql` | Back this up, since it's your entire catalogue. | | `healthcheck` | `healthcheck.sh --connect --innodb_initialized` | RomM waits for this. | -The default compose file uses MariaDB because it requires no extra driver configuration. To use PostgreSQL, swap the image for `postgres:16`, set `ROMM_DB_DRIVER=postgresql`, and point `DB_PORT` at `5432`. See [Databases](databases.md) for the full swap. +The default compose file uses MariaDB because it requires no extra driver configuration. To use PostgreSQL, swap the image for `postgres:16`, set `ROMM_DB_DRIVER=postgresql`, and point `DB_PORT` at `5432`: see [Databases](databases.md) for the full swap. ## Production hardening The quick-start compose file is functional but not production-ready. Before exposing RomM to the internet: -- **Pin image tags.** `rommapp/romm:latest` moves, so use `rommapp/romm:5.0.0` (or whatever release you're on). -- **Use a reverse proxy with HTTPS.** The built-in nginx listens on `8080` and terminates plain HTTP. Put Traefik, Caddy, or nginx in front with TLS. See [Reverse Proxy](reverse-proxy.md). -- **Set a non-default `MARIADB_ROOT_PASSWORD`.** And don't reuse it for `MARIADB_PASSWORD`. +- **Pin image tags**: `rommapp/romm:latest` moves, so use `rommapp/romm:5.0.0` (or whatever release you're on). +- **Use a reverse proxy with HTTPS**: the built-in nginx listens on `8080` and terminates plain HTTP, so put Traefik, Caddy, or nginx in front with TLS (see [Reverse Proxy](reverse-proxy.md)). +- **Set a non-default `MARIADB_ROOT_PASSWORD`**, and don't reuse it for `MARIADB_PASSWORD`. - **Mount the library read-only** unless you need RomM to write into it: `- /path/to/library:/romm/library:ro`. -- **Use Docker secrets for credentials** if your orchestrator supports them. RomM recognises `ROMM_AUTH_SECRET_KEY_FILE` for loading the secret from a file mount. -- **Enable backups.** At minimum: daily `mysqldump` + rsync of `/romm/assets` and `/romm/config`. See [Backup & Restore](backup-and-restore.md). +- **Use Docker secrets for credentials** if your orchestrator supports them: RomM recognises `ROMM_AUTH_SECRET_KEY_FILE` for loading the secret from a file mount. +- **Enable backups**: at minimum, daily `mysqldump` + rsync of `/romm/assets` and `/romm/config` (see [Backup & Restore](backup-and-restore.md)). ## Updating @@ -80,4 +80,4 @@ docker compose pull docker compose up -d ``` -Alembic migrations run automatically on startup. Read the [release notes](../releases/index.md) before every major-version bump. The 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). +Alembic migrations run automatically on startup, but read the [release notes](../releases/index.md) before every major-version bump. The 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md index 6689a3e5..daca9a05 100644 --- a/docs/platforms/ms-dos.md +++ b/docs/platforms/ms-dos.md @@ -7,21 +7,21 @@ description: How to run DOS games in RomM via dosbox-pure, homebrew, shareware d # MS-DOS -DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-pure), the EmulatorJS integration. Create a DOS platform (folder named `dos`) and drop your games in. +DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-pure), the EmulatorJS integration: create a DOS platform (folder named `dos`) and drop your games in. !!! tip "Upload games as `.zip`" - `dosbox-pure` knows how to unzip and auto-mount zipped DOS games. Much easier than packaging a raw folder. + `dosbox-pure` knows how to unzip and auto-mount zipped DOS games, which is much easier than packaging a raw folder. !!! info "Save states work" - Once you've got a game running, save state and resume from there. You only need to do the boot dance once per game. + Once you've got a game running, save state and resume from there, so you only need to do the boot dance once per game. ## Game categories -DOS games come in three flavours, each needs a different approach: +DOS games come in three flavours, each needing a different approach: -- **Homebrew:** indie / modern DOS games. Just an `.exe`, and usually Just Works once mounted. -- **Shareware demos:** what most "free DOS games" sites distribute. Same as homebrew: all files in one folder. -- **Retail:** need the original CD mounted alongside the installed game files. More work, and every game is different. +- **Homebrew:** indie / modern DOS games, just an `.exe` that usually Just Works once mounted. +- **Shareware demos:** what most "free DOS games" sites distribute, same as homebrew with all files in one folder. +- **Retail:** need the original CD mounted alongside the installed game files, which means more work and every game is different. ## The manual way (commandline) @@ -34,13 +34,13 @@ dir # find the .EXE filename.exe # launch ``` -Works for homebrew and shareware. Retail games usually fail here because they want a CD mounted somewhere. +Works for homebrew and shareware, but retail games usually fail here because they want a CD mounted somewhere. ## The automatic way (`.conf` autoload) `dosbox-pure` looks for a `.conf` file matching the `.exe` name before booting. If found, it reads the config, mounts whatever's specified, and runs the `[autoexec]` block. -Once you have a working `.conf`, click Play → game boots straight into DOS → `[autoexec]` runs → you're in the game. No typing. +Once you have a working `.conf`, click Play → game boots straight into DOS → `[autoexec]` runs → you're in the game, with no typing. ### Homebrew / demo example: DOOM shareware @@ -118,7 +118,7 @@ Walkthrough of `[autoexec]`: 3. Zip up the folder (including `DOOM.conf`). 4. Upload to RomM under the `dos` platform. -5. Click Play. Game boots. +5. Click Play, and the game boots. Blank screen after boot? Something's off in `[autoexec]`, usually a path or mount issue. @@ -164,33 +164,33 @@ Retail games need the game CD image mounted alongside the install directory. 4. Zip, upload, play. !!! info "GOG DOS re-releases don't work" - GOG's DOS games ship with custom wrappers (DOSBox Staging, ScummVM scripts, etc.) that don't translate to `dosbox-pure`. 100% failure rate observed. For GOG titles, extract the underlying game files and build a `.conf` yourself. + GOG's DOS games ship with custom wrappers (DOSBox Staging, ScummVM scripts, etc.) that don't translate to `dosbox-pure`: 100% failure rate observed. For GOG titles, extract the underlying game files and build a `.conf` yourself. ### Retail troubleshooting If the game lands on a blank DOS prompt: -1. **Remove the final `.exe` line from `[autoexec]`.** Drops you at a prompt after mounting. Check manually: +1. **Remove the final `.exe` line from `[autoexec]`**, which drops you at a prompt after mounting, then check manually: - Is the game directory mounted correctly? - `type DEFAULT.cfg` (or whatever the game's config is named): the install path should line up with what you mounted. -2. **Check drive paths.** Many retail DOS games hard-code `D:\` as the CD. If you mount the CD as `E:`, the game won't find it. -3. **Debug with native `dosbox-pure`.** Run the same zip in [RetroArch](https://retroarch.com/) with the `dosbox-pure` core. If it works there, it should work in RomM. If it doesn't, the `.conf` is the problem. +2. **Check drive paths**: many retail DOS games hard-code `D:\` as the CD, so if you mount the CD as `E:` the game won't find it. +3. **Debug with native `dosbox-pure`**: run the same zip in [RetroArch](https://retroarch.com/) with the `dosbox-pure` core. If it works there it should work in RomM, and if it doesn't the `.conf` is the problem. ## Config file deep dive -The DOOM example above is minimal. The full config can include dozens more options (sound card tweaks, CPU cycles, joysticks, IPX networking). The `dosbox-pure` wiki is the authoritative reference for every option. +The DOOM example above is minimal: the full config can include dozens more options (sound card tweaks, CPU cycles, joysticks, IPX networking), and the `dosbox-pure` wiki is the authoritative reference for every option. ## Netplay on DOS -Not supported: `dosbox-pure` works with EmulatorJS Netplay intermittently, not reliably. Stick to single-player for DOS. +Not supported: `dosbox-pure` works with EmulatorJS Netplay intermittently rather than reliably, so stick to single-player for DOS. ## Known issues -- **Mouse lag.** Enable `autolock=true` in `[sdl]`. -- **Wrong resolution / aspect.** Tweak `[render] aspect=` and `windowresolution=`. -- **Audio crackle.** Lower `rate=22050` to `rate=11025` or raise the mixer blocksize. -- **Game runs too fast.** Most auto-cycle issues. Set `cycles=fixed 4000` (or another number) explicitly. -- **Keyboard doesn't work for certain keys.** `usescancodes=false` sometimes fixes it. +- **Mouse lag**: enable `autolock=true` in `[sdl]`. +- **Wrong resolution / aspect**: tweak `[render] aspect=` and `windowresolution=`. +- **Audio crackle**: lower `rate=22050` to `rate=11025` or raise the mixer blocksize. +- **Game runs too fast**: most auto-cycle issues, so set `cycles=fixed 4000` (or another number) explicitly. +- **Keyboard doesn't work for certain keys**: `usescancodes=false` sometimes fixes it. ## Dosemu / Exodos alternatives diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 52b3c43e..297afbf1 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -47,17 +47,17 @@ You'll always set these: | Variable | Purpose | | --- | --- | -| `ROMM_AUTH_SECRET_KEY` | JWT signing key. Generate with `openssl rand -hex 32`. **Never rotate lightly**: breaks all sessions. | +| `ROMM_AUTH_SECRET_KEY` | JWT signing key generated with `openssl rand -hex 32`. **Never rotate lightly**, as it breaks all sessions. | | `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | -| `ROMM_DB_DRIVER` | `mariadb` (default), `mysql`, `postgresql`, or `sqlite`. See [Databases](../install/databases.md). | +| `ROMM_DB_DRIVER` | One of `mariadb` (default), `mysql`, `postgresql`, or `sqlite`: see [Databases](../install/databases.md). | For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../administration/metadata-providers.md). For OIDC, see [OIDC Setup](../administration/oidc/index.md). ## When env vars are read -- **Startup.** Most vars are consumed once at container start. Change requires `docker compose up -d` to apply. -- **Per-request.** A handful of feature toggles (`KIOSK_MODE`, `DISABLE_USERPASS_LOGIN`) are re-checked per request. Still, restart is safest to avoid partial-state caching. -- **Never at runtime.** There's no reload-config endpoint. +- **Startup:** most vars are consumed once at container start, so changes need a `docker compose up -d` to apply. +- **Per-request:** a handful of feature toggles (`KIOSK_MODE`, `DISABLE_USERPASS_LOGIN`) are re-checked per request, but a restart is safest to avoid partial-state caching. +- **Never at runtime:** there's no reload-config endpoint. ## Full reference diff --git a/docs/releases/upgrading-to-5.0.md b/docs/releases/upgrading-to-5.0.md index e24005f0..157f7381 100644 --- a/docs/releases/upgrading-to-5.0.md +++ b/docs/releases/upgrading-to-5.0.md @@ -76,7 +76,7 @@ rsync -a /path/to/assets/ /backup/romm/assets-pre-5.0/ rsync -a /path/to/config/ /backup/romm/config-pre-5.0/ ``` -Don't skip this. "It's a minor upgrade" isn't a thing for a major version. +Don't skip this: "it's a minor upgrade" isn't a thing for a major version. ### 4. Pin the current version @@ -130,10 +130,10 @@ docker compose logs -f romm What to watch for: -- **Alembic migrations running:** lines prefixed `INFO [alembic.runtime.migration]`. Takes anywhere from seconds (small libraries) to a couple minutes (very large ones). -- **`Application startup complete.`** RomM is healthy. -- **`Watcher started.`** Filesystem watcher up (if enabled). -- **ERROR lines:** bad. Go to [Rollback](#rollback). +- **Alembic migrations running:** lines prefixed `INFO [alembic.runtime.migration]`, taking anywhere from seconds (small libraries) to a couple minutes (very large ones). +- **`Application startup complete.`**: RomM is healthy. +- **`Watcher started.`**: filesystem watcher is up (if enabled). +- **ERROR lines:** bad news, go to [Rollback](#rollback). ### 4. Smoke-test @@ -246,7 +246,7 @@ Open a bug report on [GitHub](https://github.com/rommapp/romm/issues) with: ### Migrations hang -Watch `docker logs -f romm`. If Alembic is still running, it's still running. Large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: +Watch `docker logs -f romm`: if Alembic is still running, it's still running, and large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: - DB connection issues (`docker logs romm-db`). - Out-of-memory kills (`dmesg | grep -i oom`). @@ -266,7 +266,7 @@ See [OIDC Setup → Role mapping](../administration/oidc/index.md#role-mapping-5 ### Scheduled tasks stop firing -5.0 renamed several cron env vars. Check [Environment Variables](../reference/environment-variables.md) and update your env to the new names, otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). +5.0 renamed several cron env vars, so check [Environment Variables](../reference/environment-variables.md) and update your env to the new names: otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). ## After the upgrade diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index 5a7397b8..d83752ba 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -16,9 +16,9 @@ Fix: [clear cookies](https://support.google.com/accounts/answer/32050) for the R ## `Forbidden (403) CSRF verification failed` -CSRF protection is on by default. A mismatched or missing `csrftoken` cookie causes this. +CSRF protection is on by default, so a mismatched or missing `csrftoken` cookie causes this. -1. Reload the page. RomM sets a fresh CSRF cookie on GET requests, which should fix it on the next POST. +1. Reload the page: RomM sets a fresh CSRF cookie on GET requests, which should fix it on the next POST. 2. Still broken? Clear cookies for the RomM host and hard-reload (`CMD+SHIFT+R` / `CTRL+F5`). 3. Known to happen on Chrome specifically, and rare on Firefox/Safari. @@ -26,11 +26,11 @@ If you're behind a reverse proxy and CSRF keeps failing, the proxy is probably s ## `400 Bad Request` on the WebSocket endpoint -Your reverse proxy is stripping the WebSocket upgrade. RomM uses Socket.IO for live updates (scan progress, Netplay). +Your reverse proxy is stripping the WebSocket upgrade, and RomM uses Socket.IO for live updates (scan progress, Netplay). Fixes per proxy: -- **Nginx / NPM**: enable WebSockets Support. The [Reverse Proxy](../install/reverse-proxy.md) snippets already do this. +- **Nginx / NPM**: enable WebSockets Support (the [Reverse Proxy](../install/reverse-proxy.md) snippets already do this). - **Traefik**: add `proxy_set_header Upgrade $http_upgrade` (or use the Traefik middleware equivalent). - **Caddy**: WebSockets work out of the box with `reverse_proxy`. - **Cloudflare**: enable **WebSockets** under Network settings. @@ -65,10 +65,10 @@ The `OIDC_REDIRECT_URI` in RomM's env doesn't **exactly** match what's registere Check for: -- **Trailing slashes.** `/api/oauth/openid` vs `/api/oauth/openid/` are different to the IdP. -- **Scheme.** `http://` vs `https://`. -- **Host.** `romm.example.com` vs `www.romm.example.com` vs the bare IP. -- **Port.** Implied `80` / `443` on HTTPS vs an explicit port. +- **Trailing slashes**: `/api/oauth/openid` vs `/api/oauth/openid/` are different to the IdP. +- **Scheme**: `http://` vs `https://`. +- **Host**: `romm.example.com` vs `www.romm.example.com` vs the bare IP. +- **Port**: implied `80` / `443` on HTTPS vs an explicit port. Fix: make them identical on both sides. @@ -77,10 +77,10 @@ Fix: make them identical on both sides. You configured `OIDC_CLAIM_ROLES` but RomM isn't honouring it. 1. **Is the claim actually in the token?** Decode your IdP's ID token at [jwt.io](https://jwt.io) and verify the claim name (e.g. `groups`, `realm_access.roles`) is present and non-empty. -2. **Does the value match?** `OIDC_ROLE_ADMIN=romm-admin` will only match if the claim contains exactly the string `romm-admin`. Case-sensitive. +2. **Does the value match?** `OIDC_ROLE_ADMIN=romm-admin` will only match if the claim contains exactly the string `romm-admin`, and it's case-sensitive. 3. **Is the claim mapper on the IdP side configured to include the claim?** On Keycloak, for example, you need a Client Scope with a Group Membership mapper added to the client. -Roles are re-evaluated on every login, and there's no cache to bust. Log out and back in after fixing. +Roles are re-evaluated on every login, with no cache to bust, so log out and back in after fixing. ### "Email is missing from token" (Zitadel-specific) @@ -90,7 +90,7 @@ See [OIDC with Zitadel → Enable claims](../administration/oidc/zitadel.md) for ### Authentik 2025.10: login succeeds but RomM rejects the user -Authentik 2025.10 changed the default `email_verified` claim from `true` to `false`. RomM requires a verified email, so the claim must arrive as `true`. +Authentik 2025.10 changed the default `email_verified` claim from `true` to `false`, but RomM requires a verified email so the claim must arrive as `true`. Fix: add the property mapping documented in [OIDC with Authentik → Create a property mapping](../administration/oidc/authentik.md#2-create-a-property-mapping-authentik-202510). @@ -98,12 +98,12 @@ Fix: add the property mapping documented in [OIDC with Authentik → Create a pr Two possibilities: -1. **Email not verified in Keycloak.** Admin Console → Users → open the user → **Email Verified**: on. RomM rejects unverified emails. -2. **Email mismatch between Keycloak and a pre-existing RomM user.** If RomM already has a local account `alice@example.com`, the first OIDC login for `alice@example.com` signs into that account. If the emails don't match exactly, RomM creates a *second* account. Fix: edit the user in RomM to set the correct email, then log in via OIDC. +1. **Email not verified in Keycloak**: Admin Console → Users → open the user → **Email Verified**: on. RomM rejects unverified emails. +2. **Email mismatch between Keycloak and a pre-existing RomM user**: if RomM already has a local account `alice@example.com`, the first OIDC login for `alice@example.com` signs into that account. If the emails don't match exactly, RomM creates a *second* account. Fix: edit the user in RomM to set the correct email, then log in via OIDC. ### `OAuthException: expired token` on callback -Your RomM host and the IdP have significant clock drift. Run NTP on both. +Your RomM host and the IdP have significant clock drift, so run NTP on both. ### Autologin loops forever @@ -120,5 +120,5 @@ If `bypass_autologin` doesn't work in your version, shell into the container and ## Still stuck - Check the container logs: `docker logs romm 2>&1 | grep -iE 'auth|oidc|oauth'`. -- Cross-reference your IdP's audit logs. They often show exactly why a login was rejected on their side. +- Cross-reference your IdP's audit logs, which often show exactly why a login was rejected on their side. - Ask on [Discord](https://discord.gg/romm) `#help` with the IdP name and the exact error text. diff --git a/docs/troubleshooting/scanning.md b/docs/troubleshooting/scanning.md index cdb088a3..2a7f31d6 100644 --- a/docs/troubleshooting/scanning.md +++ b/docs/troubleshooting/scanning.md @@ -11,13 +11,13 @@ Most scan problems boil down to: library not mounted where RomM expects, folder Three common causes: -1. **Library mounted in the wrong place.** RomM expects your library at `/romm/library` inside the container. Verify with `docker exec romm ls /romm/library`. You should see your `roms/` directory (Structure A) or platform folders (Structure B). -2. **Permissions.** The RomM container has to read the files. Check from the host: `ls -lh /path/to/library`. If the RomM process can't see them, nothing scans. -3. **Invalid folder structure.** Review [Folder Structure](../getting-started/folder-structure.md). RomM tries Structure A first, so if it finds neither layout, the scan completes in seconds with nothing found. +1. **Library mounted in the wrong place**: RomM expects your library at `/romm/library` inside the container. Verify with `docker exec romm ls /romm/library`: you should see your `roms/` directory (Structure A) or platform folders (Structure B). +2. **Permissions**: the RomM container has to read the files, so check from the host with `ls -lh /path/to/library`. If the RomM process can't see them, nothing scans. +3. **Invalid folder structure**: review [Folder Structure](../getting-started/folder-structure.md). RomM tries Structure A first, so if it finds neither layout the scan completes in seconds with nothing found. ## "ROMs not found for platform X, check romm folder structure" -Same root cause as the previous one. RomM needs a `roms/` directory somewhere: either `/romm/library/roms/{platform}/` (Structure A) or `/romm/library/{platform}/roms/` (Structure B). +Same root cause as the previous one: RomM needs a `roms/` directory somewhere, either `/romm/library/roms/{platform}/` (Structure A) or `/romm/library/{platform}/roms/` (Structure B). Common mount-path mistakes: @@ -35,7 +35,7 @@ Common mount-path mistakes: ## Platform folder isn't detected -RomM matches your platform folder names against IGDB platform slugs. If your folder is named differently, it won't match. +RomM matches your platform folder names against IGDB platform slugs, so if your folder is named differently, it won't match. Example: Nintendo 64DD's IGDB slug is `64dd` ([igdb.com/platforms/64dd](https://www.igdb.com/platforms/64dd)), so the folder should be named `64dd/`. If your folder is `n64dd/` or `nintendo-64dd/`, it won't detect. @@ -54,10 +54,10 @@ Full list of supported slugs: [Supported Platforms](../platforms/supported-platf ## Scan times out at ~4 hours (or whatever `SCAN_TIMEOUT` says) -Scans are capped. If yours hits the cap: +Scans are capped, so if yours hits the cap: -1. **Use `Quick` mode** from the Scan page: skips already-catalogued files. Most repeated scans complete in minutes. -2. **Raise the cap** if you need a full rescan: `SCAN_TIMEOUT_HOURS=8` (or whatever). See [Environment Variables](../reference/environment-variables.md). +1. **Use `Quick` mode** from the Scan page, which skips already-catalogued files so most repeated scans complete in minutes. +2. **Raise the cap** if you need a full rescan: `SCAN_TIMEOUT_HOURS=8` (or whatever), per [Environment Variables](../reference/environment-variables.md). 3. **Run scans per-platform** instead of everything at once, to checkpoint progress. ## Scan stops mid-platform @@ -71,8 +71,8 @@ docker logs romm 2>&1 | grep -E 'ERROR.*scan_handler' Common culprits: - **Corrupted file**: unzip it, re-zip, try again. -- **Old DOS zip with backslash paths**: Python's `zipfile` chokes on these. Re-create the archive with forward slashes. -- **Read errors on the mount**: usually SMB/NFS flakiness. Watch `dmesg` for mount drops. +- **Old DOS zip with backslash paths**: Python's `zipfile` chokes on these, so re-create the archive with forward slashes. +- **Read errors on the mount**: usually SMB/NFS flakiness, so watch `dmesg` for mount drops. Log lines look like: @@ -82,7 +82,7 @@ ERROR: [RomM][scan_handler][2025-04-12 11:48:55] Failed to process /romm/li ## Tracking progress on a big library -When scans run for hours across many platforms, the live UI log is hard to follow. Tail the container logs instead: +When scans run for hours across many platforms, the live UI log is hard to follow, so tail the container logs instead: ```sh docker logs romm 2>/dev/null | grep -E 'scan_handler.*Identified as' @@ -101,9 +101,9 @@ INFO: [RomM][scan_handler][2025-04-13 12:50:42] Identified as WonderSwan Check the scan log for per-provider errors: - **IGDB: "Could not get twitch auth token"** → `IGDB_CLIENT_ID` or `IGDB_CLIENT_SECRET` is wrong, or revoked on the Twitch side. -- **ScreenScraper: "403 / DAILY_LIMIT_REACHED"** → you've hit the free-tier quota. Wait, or upgrade your ScreenScraper membership. +- **ScreenScraper: "403 / DAILY_LIMIT_REACHED"** → you've hit the free-tier quota, so wait or upgrade your ScreenScraper membership. - **RetroAchievements: "Invalid API key"** → regenerate at [retroachievements.org/settings](https://retroachievements.org/settings). -- **Any provider: "Request timed out"** → transient. Re-run an **Unmatched** scan to retry only the failures. +- **Any provider: "Request timed out"** → transient, so re-run an **Unmatched** scan to retry only the failures. Full provider reference: [Metadata Providers](../administration/metadata-providers.md). @@ -111,18 +111,18 @@ Full provider reference: [Metadata Providers](../administration/metadata-provide Options, in order of effort: -1. **Add more providers.** A ROM IGDB doesn't know about might be in ScreenScraper, Hasheous, or LaunchBox. Enable one or more, then run an **Unmatched** scan. -2. **Use filename tags.** If you already know the provider ID, rename the file to include `(igdb-1234)` or similar. See [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). -3. **Manually match.** Open the ROM detail page, click the **Match** button, and search for the right title. +1. **Add more providers**: a ROM IGDB doesn't know about might be in ScreenScraper, Hasheous, or LaunchBox, so enable one or more, then run an **Unmatched** scan. +2. **Use filename tags**: if you already know the provider ID, rename the file to include `(igdb-1234)` or similar (see [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames)). +3. **Manually match**: open the ROM detail page, click the **Match** button, and search for the right title. ## Hash calculations are slow -Hashing large ROMs (PS1, Saturn, DC images) is IO-bound. Options: +Hashing large ROMs (PS1, Saturn, DC images) is IO-bound, with a few options: -- **Skip hashing on small hosts**: check **Skip hash calculation** on the Scan page, or set `filesystem.skip_hash_calculation: true` in `config.yml`. You lose RetroAchievements and Hasheous matching (both depend on hashes). +- **Skip hashing on small hosts**: check **Skip hash calculation** on the Scan page, or set `filesystem.skip_hash_calculation: true` in `config.yml`. You lose RetroAchievements and Hasheous matching, since both depend on hashes. - **Use SSD/NVMe for the library** if you care about hash performance. - **Raise `SEVEN_ZIP_TIMEOUT`** if you're scanning many large `.7z` archives. ## Scan seems to work but nothing shows up -Refresh the page. The ribbons on the home dashboard cache aggressively during a scan. Still blank? Check **Administration → Server Stats**. If counts are zero, the scan didn't actually persist anything. Usually a DB permission issue, so check `docker logs romm 2>&1 | grep -i 'database'`. +Refresh the page, since the ribbons on the home dashboard cache aggressively during a scan. Still blank? Check **Administration → Server Stats**: if counts are zero, the scan didn't actually persist anything, usually a DB permission issue, so check `docker logs romm 2>&1 | grep -i 'database'`. diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index d440adef..255ec45e 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -26,7 +26,7 @@ OIDC users don't have local passwords, because authentication is via the IdP. Pa ### Avatar -Upload a PNG / JPEG / WebP. Displayed next to your name across the UI. +Upload a PNG / JPEG / WebP, and it shows next to your name across the UI. ### Preferred username in OIDC @@ -36,19 +36,19 @@ If you're an OIDC user and want RomM to show your `preferred_username` from the Long-lived API tokens for companion apps, scripts, and integrations. Each token is scoped to a subset of your user's scopes, and optionally expires. -See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md). This section is the "how I create one from the UI" version. +See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md); this section is just the "how I create one from the UI" version. ### Creating a token 1. **Profile → Client API Tokens → + New Token**. 2. Pick: - **Name**: descriptive (e.g. "Grout on my RG35XX"). - - **Scopes**: which permissions to include. Default is read-only. Tick write scopes deliberately. - - **Expiry**: optional. Blank = never expires. + - **Scopes**: which permissions to include. Default is read-only, so tick write scopes deliberately. + - **Expiry**: optional, blank = never expires. 3. **Create**. -4. The token appears **once**. Copy it immediately, because you can't retrieve it later. +4. The token appears **once**, so copy it immediately because you can't retrieve it later. -Token format: `rmm_` + 40 hex chars. Treat it like a password. +Token format: `rmm_` + 40 hex chars, and you should treat it like a password. ### Pairing to a device @@ -65,12 +65,12 @@ Full flow in [Client API Tokens](../ecosystem/client-api-tokens.md). ### Limits -- **25 active tokens per user.** Delete old ones to free slots. -- **Tokens carry a subset of your scopes.** If an admin demotes you to Viewer, any token you hold with admin scopes stops working. Tokens don't escalate privileges beyond the owning user's current role. +- **25 active tokens per user**, so delete old ones to free slots. +- **Tokens carry a subset of your scopes**, so if an admin demotes you to Viewer, any token you hold with admin scopes stops working. Tokens don't escalate privileges beyond the owning user's current role. ### Revoking -**Profile → Client API Tokens → [token] → Revoke.** Takes effect immediately. The companion app loses access on its next API call. +**Profile → Client API Tokens → [token] → Revoke** takes effect immediately, and the companion app loses access on its next API call. ## Keyboard shortcuts diff --git a/docs/using/downloads.md b/docs/using/downloads.md index 8f4d88ac..2bfcdd7d 100644 --- a/docs/using/downloads.md +++ b/docs/using/downloads.md @@ -9,7 +9,7 @@ description: Download ROMs from RomM: single, bulk, QR codes, copy-link, and str The quick path: hover a game card → click **Download**. Or from the game detail page → **Download** button. -RomM streams the file directly: no temp file on disk, no copy, no waiting for packaging. Large ROMs and multi-disc sets download just as quickly as small ones. +RomM streams the file directly, with no temp file on disk, no copy, and no waiting for packaging, so large ROMs and multi-disc sets download just as quickly as small ones. For multi-file games (folder-based), RomM streams a zip on the fly. See [Multi-file downloads](#multi-file-and-bulk-downloads-nginx-mod_zip) below. @@ -19,7 +19,7 @@ For cases where you want the URL, not the file right now: sending it to another Context menu (…) on a game card → **Copy download link**, and the URL is on your clipboard. -Anyone with access to the link and the server can download. By default the link requires your session cookie or a bearer token. See [Third-party download auth](#third-party-download-auth) for the exception. +Anyone with access to the link and the server can download, though by default the link requires your session cookie or a bearer token. See [Third-party download auth](#third-party-download-auth) for the exception. ## QR code @@ -27,7 +27,7 @@ For handheld-to-desktop or desktop-to-phone transfers without typing a URL. Context menu (…) → **Show QR Code**, then point the other device's camera at the screen. -The QR decodes to the same URL as Copy Link. Same auth rules apply. +The QR decodes to the same URL as Copy Link, and the same auth rules apply. ### Nintendo 3DS direct install @@ -45,13 +45,13 @@ RomM's nginx is built with `mod_zip`, which streams a zip archive over HTTP with ### Multi-disc / multi-file games -When a game is stored as a folder (multi-disc, game + DLC, game + patch, etc.), clicking **Download** builds a zip on the fly containing the whole folder. The browser sees a zip download start immediately, no "packaging…" delay. +When a game is stored as a folder (multi-disc, game + DLC, game + patch, etc.), clicking **Download** builds a zip on the fly containing the whole folder, and the browser sees a zip download start immediately with no "packaging…" delay. ### Bulk download from a gallery Multi-select ROMs in any gallery (platform view, collection, search results) → toolbar → **Download selected**, producing a single zip with every selected ROM preserved in its platform folder structure. -No practical limit: the zip is streamed, so memory doesn't grow with selection size. Disk I/O and network bandwidth are the actual limits. +No practical limit: the zip is streamed so memory doesn't grow with selection size, and disk I/O and network bandwidth are the actual limits. ## Third-party download auth @@ -81,7 +81,7 @@ Some emulators can take an HTTP URL directly. Point them at the same URL the Cop ## Download history -Not tracked in 5.0. RomM doesn't log downloads for privacy reasons. Use your reverse proxy's access log if you need to audit. +Not tracked in 5.0: RomM doesn't log downloads for privacy reasons, so use your reverse proxy's access log if you need to audit. ## Troubleshooting diff --git a/docs/using/library.md b/docs/using/library.md index 08944ac6..4dadbf13 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -11,7 +11,7 @@ The library is the heart of RomM. This page covers the day-to-day UI: the dashbo ## Dashboard -Home screen. Several **ribbons** of content: +Home screen, with several **ribbons** of content: - **Recently Added**: a carousel of the latest ROMs RomM has indexed. - **Continue Playing**: games with an active play session. See [Play Sessions](../getting-started/concepts.md#play-session). @@ -22,7 +22,7 @@ Each ribbon can be hidden or shown from **Profile → User Interface**, handy if ## Menu bar -Visible everywhere. Shortcuts to: +Visible everywhere, with shortcuts to: - **Search**: global search across every ROM, metadata field, and tag. - **Platforms**: drawer listing every platform as a link. @@ -34,7 +34,7 @@ Visible everywhere. Shortcuts to: ## Grid/list toggle -Every gallery (platform view, collection view, search results) has a grid-vs-list toggle in the top right. Grid is the default: card thumbnails at scale. List is denser: one row per ROM, sortable columns for title, release date, rating, playtime, region. +Every gallery (platform view, collection view, search results) has a grid-vs-list toggle in the top right. Grid is the default (card thumbnails at scale), while list is denser: one row per ROM with sortable columns for title, release date, rating, playtime, and region. ## Game cards From 71dcdfe3f257c52ec47481953d52c0ea841a4cb8 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 10:35:07 -0400 Subject: [PATCH 037/121] cleanup releases --- docs/releases/changelog.md | 33 +--- docs/releases/index.md | 2 +- docs/releases/upgrading-to-3.0.md | 91 +--------- docs/releases/upgrading-to-4.0.md | 6 + docs/releases/upgrading-to-5.0.md | 274 +----------------------------- 5 files changed, 12 insertions(+), 394 deletions(-) create mode 100644 docs/releases/upgrading-to-4.0.md diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md index 960c0386..3baa05e7 100644 --- a/docs/releases/changelog.md +++ b/docs/releases/changelog.md @@ -10,50 +10,23 @@ This page summarises major RomM releases. **The authoritative per-release change For migration-grade detail on a major version, go straight to that version's guide: - **[Upgrading to 5.0](upgrading-to-5.0.md)** +- **[Upgrading to 4.0](upgrading-to-4.0.md)** - **[Upgrading to 3.0](upgrading-to-3.0.md)** ## 5.0 -[What's New in 5.0](../getting-started/what-is-new-in-5.md) · [Upgrading to 5.0](upgrading-to-5.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/5.0.0) +[Upgrading to 5.0](upgrading-to-5.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/5.0.0) -The biggest release since the project started. Highlights: - -- **Console Mode:** a gamepad-first `/console` UI for TVs. -- **Smart & Virtual Collections:** rule-based and auto-generated collections. -- **ROM Patcher:** apply IPS/UPS/BPS/PPF and more in-browser. -- **Netplay:** EmulatorJS multiplayer with ICE servers. -- **Device sync protocol** + Client API Tokens with device pairing. -- **OIDC role mapping** via claims (`OIDC_CLAIM_ROLES`). -- **Thirteen metadata providers:** added TheGamesDB, Libretro, gamelist.xml importer. -- **PWA install** support. -- **19 locales**. -- **OpenTelemetry** export via `OTEL_ENABLED`. -- **Slim + Full image variants**. ## 4.x -Highlights rolled up: - -- **4.8:** stability and per-platform stats opt-in. -- **4.x:** iterative improvements on the 3.x → 4.x baseline: expanded metadata sources, frontend polish, first-class companion-app support. -The 4.x docs live at [docs.romm.app/4.8/](https://docs.romm.app/4.8/) as a frozen snapshot. See [Release Notes & Migration → Docs versions](index.md#docs-versions). +[Upgrading to 4.0](upgrading-to-4.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/4.0.0) ## 3.0 [Upgrading to 3.0](upgrading-to-3.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/3.0.0) -Watershed release that made RomM a real multi-user platform: - -- **Required authentication:** no more unauthenticated mode. -- **SQLite support dropped:** MariaDB became the default. -- **Redis built-in:** the experimental Redis add-on was absorbed. -- **Saves, states, and screenshots:** first-class user asset management. -- **Config moved to a folder mount** (`/romm/config`). - -## Older - -2.x and earlier aren't supported and aren't covered by migration guides. Upgrade to 3.x using the 3.0 guide, then to 5.x using the 5.0 guide. ## Want release pings? diff --git a/docs/releases/index.md b/docs/releases/index.md index 70d453e6..8e6b0655 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -7,7 +7,7 @@ description: RomM release cadence, versioning policy, docs versions, and per-maj ## Current version -RomM's current stable is [`5.0.0`](changelog.md). Read [What's New in 5.0](../getting-started/what-is-new-in-5.md) for the highlights, or [Upgrading to 5.0](upgrading-to-5.0.md) if you're coming from 4.x. +RomM's current stable is [`5.0.0`](changelog.md). Read [Upgrading to 5.0](upgrading-to-5.0.md) if you're coming from 4.x. ## Versioning diff --git a/docs/releases/upgrading-to-3.0.md b/docs/releases/upgrading-to-3.0.md index f99d546f..00eb2d09 100644 --- a/docs/releases/upgrading-to-3.0.md +++ b/docs/releases/upgrading-to-3.0.md @@ -1,95 +1,6 @@ --- title: Upgrading to 3.0 -description: Historical migration guide for 2.x → 3.0: SQLite drop, auth required, Redis built-in, config folder mount, assets volume. +description: Migrate from RomM 2.x to 3.0 --- # Upgrading to 3.0 - -!!! info "This is a historical guide" - RomM 3.0 shipped a while ago. If you're on 2.x, still use this page. If you're on 3.x / 4.x, see [Upgrading to 5.0](upgrading-to-5.0.md). - -Version 3.0 introduced several breaking changes aimed at improving performance and unlocking new features (EmulatorJS, in-browser saves/states). Read this entire page before pulling the new image. Skipping steps will make RomM inaccessible or unresponsive. - -All the changes below are reflected in the example [docker-compose.yml](https://github.com/rommapp/romm/blob/master/examples/docker-compose.example.yml), which was significantly simplified in 3.0. - -## Dropped SQLite support - -SQLite was removed because of ongoing engineering issues. MariaDB is stabler and the only supported default. If you were on SQLite, RomM will auto-migrate your data to MariaDB, **but you need to make the following changes before upgrading**. - -In your environment variables, set `ROMM_DB_DRIVER` to `mariadb` (or drop the variable entirely, it's no longer required) and add: - -```yaml -environment: - - DB_HOST=mariadb - - DB_PORT=3306 - - DB_NAME=romm # matches MYSQL_DATABASE in mariadb - - DB_USER=romm-user # matches MYSQL_USER - - DB_PASSWD= # matches MYSQL_PASSWORD -``` - -Set up the MariaDB container using the [reference compose](../install/docker-compose.md). - -## Authentication is now required - -To support EmulatorJS, save/state management, and multi-user features, 3.0 requires authentication. If you were running with auth disabled, remove `ROMM_AUTH_ENABLED` and add: - -```yaml -environment: - - ROMM_AUTH_SECRET_KEY= -``` - -We know this breaks the "unrestricted sharing" pattern some users relied on. See [Kiosk Mode](../administration/authentication.md#kiosk-mode) for a read-only anonymous-access option that ships with RomM. - -## Redis is built in - -The 2.x "experimental Redis" add-on container is gone. RomM ships with Redis internally. Remove the external Redis container and these environment variables: - -```yaml -# Remove: -# - ENABLE_EXPERIMENTAL_REDIS -# - REDIS_HOST -# - REDIS_PORT -``` - -If you want an external Redis instance anyway (recommended for production), keep `REDIS_HOST` / `REDIS_PORT`. See [Redis or Valkey](../install/redis-or-valkey.md). - -## Configuration folder - -`config.yml` is now mounted via a **config folder**, not a single file mount. - -Place your existing `config.yml` inside a directory and bind that directory to `/romm/config`: - -```yaml -volumes: - - /path/to/config:/romm/config -``` - -Updated example: [config.example.yml](https://github.com/rommapp/romm/blob/master/examples/config.example.yml). See also [Configuration File](../reference/configuration-file.md). - -## New `/romm/assets` volume - -3.0 introduced save, state, and screenshot management. Uploads land in `/romm/assets` inside the container, so mount a host path so they persist: - -```yaml -volumes: - - /path/to/assets:/romm/assets -``` - -Put this next to the folder you mount as `/romm/library` so all RomM-owned data stays together. - -## After the upgrade - -- Visit the web UI. You'll see a Setup Wizard if you had auth disabled previously. Complete it to create the first admin. -- Run a scan to re-match any games that lost metadata during the SQLite → MariaDB migration. -- Start using saves/states. They'll live under `/romm/assets` from now on. - -## Rollback - -If something breaks: - -1. Restore your old `docker-compose.yml` / env file. -2. Restore your SQLite DB file backup (if you were on SQLite). -3. Pull the 2.x image tag explicitly (`rommapp/romm:2.x.y`). -4. `docker compose up -d`. - -Going forward, the 3.x / 4.x / 5.x chain of migrations is documented in their respective guides. Start with [Upgrading to 5.0](upgrading-to-5.0.md) once you're on 3.x. diff --git a/docs/releases/upgrading-to-4.0.md b/docs/releases/upgrading-to-4.0.md new file mode 100644 index 00000000..f135d8d8 --- /dev/null +++ b/docs/releases/upgrading-to-4.0.md @@ -0,0 +1,6 @@ +--- +title: Upgrading to 4.0 +description: Migrate from RomM 3.x to 4.0 +--- + +# Upgrading to 4.0 diff --git a/docs/releases/upgrading-to-5.0.md b/docs/releases/upgrading-to-5.0.md index 157f7381..96e90fa2 100644 --- a/docs/releases/upgrading-to-5.0.md +++ b/docs/releases/upgrading-to-5.0.md @@ -1,278 +1,6 @@ --- title: Upgrading to 5.0 -description: Migrate from RomM 4.x to 5.0: breaking changes, env var renames, pre-flight checklist, and rollback. +description: Migrate from RomM 4.x to 5.0 --- # Upgrading to 5.0 - -Migration guide for **RomM 4.x → 5.0**. If you're on 2.x or earlier, upgrade to 3.x first via [Upgrading to 3.0](upgrading-to-3.0.md), then come back here. - -!!! danger "Read this entire page before upgrading" - 5.0 is a major release with schema migrations and a handful of breaking changes. Skipping the pre-flight checklist will lose data or break your instance. - -!!! note "This guide is finalised at 5.0 GA" - Some sections below reference exact env-var renames and schema changes that will be confirmed against the final 5.0.0 source tag. If you're reading this before 5.0 GA, treat it as a structural outline. The commands are correct, specific rename lists will be filled in as the release tag is cut. - -## What changes in 5.0 - -### Summary - -- **Schema migration:** Alembic runs automatically on startup. Irreversible without a backup. -- **New env vars**, some renamed (see [Env var migration table](#env-var-migration-table)). -- **`config.yml` gains new sections:** `scan.region`, `scan.language`, `scan.media`, `scan.gamelist.export`, `scan.pegasus.export`, `emulatorjs.*`, `filesystem.firmware_folder`. Old settings keep working untouched. -- **Image tags add `:slim` variants:** no change to existing `:latest` / `:5.0.0`. -- **Docs URL structure:** every old URL redirects automatically. External links continue working. - -### Big new features - -Highlights only. Full list in [What's New in 5.0](../getting-started/what-is-new-in-5.md): - -- Console Mode (`/console`, TV/gamepad UI). -- Smart and Virtual Collections. -- ROM Patcher. -- Netplay with ICE servers. -- PWA install. -- 19 locales. -- Device Sync protocol (bidirectional push-pull for handhelds). -- Client API Tokens with device pairing. -- OIDC role mapping via claims. -- Expanded metadata providers: TheGamesDB, Libretro, gamelist.xml importer. -- OpenTelemetry export. - -## Pre-flight checklist - -**Do not skip.** - -### 1. Read - -- This whole page. -- [What's New in 5.0](../getting-started/what-is-new-in-5.md). -- The release's [GitHub Release notes](https://github.com/rommapp/romm/releases). - -### 2. Check you're on a recent 4.x - -5.0's migrations assume you've already run the 4.x migrations. From a 4.7 or 4.8 baseline is ideal. If you're on 3.x: - -1. Upgrade to the latest 4.x first (`rommapp/romm:4.8.1` at time of writing). -2. Verify the instance works. -3. Then upgrade to 5.0. - -Don't jump 3.x → 5.0 in one step. - -### 3. Back up - -See [Backup & Restore](../install/backup-and-restore.md). At minimum: - -```sh -# MariaDB -docker exec romm-db mariadb-dump --user=romm-user --password=$DB_PASSWD \ - --single-transaction --databases romm > romm-db-pre-5.0.sql - -# or Postgres -docker exec romm-db pg_dump --username=romm-user --dbname=romm > romm-db-pre-5.0.sql - -# Assets + config -rsync -a /path/to/assets/ /backup/romm/assets-pre-5.0/ -rsync -a /path/to/config/ /backup/romm/config-pre-5.0/ -``` - -Don't skip this: "it's a minor upgrade" isn't a thing for a major version. - -### 4. Pin the current version - -Before pulling 5.0, change your `docker-compose.yml` to pin the old version explicitly so you can roll back: - -```yaml -image: rommapp/romm:4.8.1 # whatever you're actually on -``` - -Commit this to version control if you track your compose file. Saves guesswork on rollback. - -### 5. Block access while you upgrade - -Optional but recommended for shared instances. Take RomM offline for ~5 minutes: - -```sh -# Return 503 at the reverse proxy while upgrading -``` - -Prevents users hitting mid-migration state. - -## Upgrade steps - -### 1. Pull the new image - -```sh -docker pull rommapp/romm:5.0.0 -``` - -Or pick the slim variant: - -```sh -docker pull rommapp/romm:5.0.0-slim -``` - -### 2. Update your compose file - -Edit `docker-compose.yml`: - -- Change the image tag to `rommapp/romm:5.0.0` (or `:5.0.0-slim`). -- Add any new env vars relevant to your setup from the [migration table](#env-var-migration-table). -- Rename any renamed env vars. -- Double-check volumes are still correct. - -### 3. Bring it up - -```sh -docker compose up -d -docker compose logs -f romm -``` - -What to watch for: - -- **Alembic migrations running:** lines prefixed `INFO [alembic.runtime.migration]`, taking anywhere from seconds (small libraries) to a couple minutes (very large ones). -- **`Application startup complete.`**: RomM is healthy. -- **`Watcher started.`**: filesystem watcher is up (if enabled). -- **ERROR lines:** bad news, go to [Rollback](#rollback). - -### 4. Smoke-test - -Don't trust the migration until you've verified: - -- Log in as an Admin. -- **Administration → Server Stats:** counts look reasonable, no massive loss. -- Open a platform with lots of games and check a few games have metadata. -- Open a game's details. Personal tab shows your rating / playtime if you had any. -- Check **Administration → Users**. Your accounts are intact. -- Run a **Quick** scan. Should complete cleanly in a few seconds. -- (If you had OIDC) log out, log in via OIDC. Should pick up `OIDC_CLAIM_ROLES` if configured. - -## Env var migration table - -| 4.x name | 5.0 name | Change | -| --- | --- | --- | -| `SCREENSCRAPER_USER` | `SCREENSCRAPER_USER` | No change. | -| `IGDB_CLIENT_ID` | `IGDB_CLIENT_ID` | No change. | -| _(new in 5.0)_ | `ALLOW_PUBLIC_REGISTRATION` | Opt-in public signup. Off by default. | -| _(new in 5.0)_ | `INVITE_TOKEN_DAYS` | Invite link expiry (days). Default 30. | -| _(new in 5.0)_ | `OIDC_CLAIM_ROLES` | Which claim to read for role mapping. | -| _(new in 5.0)_ | `OIDC_ROLE_VIEWER` / `OIDC_ROLE_EDITOR` / `OIDC_ROLE_ADMIN` | Map OIDC group names to RomM roles. | -| _(new in 5.0)_ | `ENABLE_NETPLAY` | Toggle Netplay (default on). | -| _(new in 5.0)_ | `WATCHER_ENABLED` | Filesystem watcher (default on). | -| _(new in 5.0)_ | `WATCH_EXTENSIONS_ONLY` | Watcher filters by extension. | -| _(new in 5.0)_ | `RESCAN_ON_FILESYSTEM_CHANGE` / `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` | Watcher debounce. | -| _(new in 5.0)_ | `OTEL_ENABLED` | Opt into OpenTelemetry export. | -| _(new in 5.0)_ | `SSH_PRIVATE_KEY_PATH` | Path to SSH key used by Push-Pull Device Sync. | -| _(new in 5.0)_ | `TGDB_API_ENABLED` | Enable TheGamesDB as a metadata source. | -| _(new in 5.0)_ | `RETROACHIEVEMENTS_SYNC_INTERVAL_CRON` / `NETPLAY_CLEANUP_INTERVAL_CRON` / `PUSH_PULL_SYNC_INTERVAL_CRON` / `IMAGE_CONVERSION_INTERVAL_CRON` / `SWITCH_TITLEDB_FETCH_INTERVAL_CRON` | New scheduled-task cron controls. | - -Full list in [Environment Variables](../reference/environment-variables.md). The reference page is autogenerated from the upstream `env.template`, so it stays correct even as more vars land in 5.x patches. - -## `config.yml` changes - -`config.yml` gains new sections, and everything else stays backwards compatible. - -- **`scan.region`:** region preference order (array). -- **`scan.language`:** language preference order (array). -- **`scan.media`:** which media types to fetch (`[box2d, screenshot, manual]`, etc.). -- **`scan.gamelist.export`** + **`scan.gamelist.media.*`:** export gamelist.xml for ES-DE/Batocera. -- **`scan.pegasus.export`:** export metadata.pegasus.txt for Pegasus. -- **`filesystem.firmware_folder`:** override the `bios` folder name. -- **`emulatorjs.netplay.ice_servers`:** STUN/TURN for Netplay. -- **`emulatorjs.settings`** / **`emulatorjs.controls`:** per-core emulator config and button mappings. -- **`emulatorjs.cache_limit`** / **`emulatorjs.disable_batch_bootup`** / **`emulatorjs.disable_auto_unload`**. - -Full schema: [Configuration File](../reference/configuration-file.md). - -## Docs URL changes - -The docs site restructured in 5.0. Every old URL redirects to its new home: - -- `/latest/Getting-Started/Quick-Start-Guide/` → `/latest/getting-started/quick-start/` -- `/latest/System-Setup/Synology-Setup-Guide/` → `/latest/install/synology/` -- `/latest/Usage/UserManagement/` → `/latest/administration/users-and-roles/` -- `/latest/OIDC-Guides/OIDC-Setup-With-Keycloak/` → `/latest/administration/oidc/keycloak/` - -And so on. Every page in the 4.x IA is covered. External links in forum posts, blog articles, issue threads, and Discord messages keep working. Internal bookmarks to `/latest///` hit a 301 to the new slug automatically. - -## Rollback - -If something's clearly broken after the upgrade: - -### 1. Stop the stack - -```sh -docker compose down -``` - -### 2. Revert the image tag - -Edit `docker-compose.yml`: change `rommapp/romm:5.0.0` back to whatever you were on (`rommapp/romm:4.8.1`). - -### 3. Restore the DB - -```sh -# MariaDB / MySQL -docker compose up -d romm-db # DB only -docker exec -i romm-db mariadb --user=root --password=$ROOT_PW romm \ - < romm-db-pre-5.0.sql - -# Postgres -docker compose up -d romm-db -docker exec -i romm-db psql --username=romm-user --dbname=romm \ - < romm-db-pre-5.0.sql -``` - -### 4. Restore assets + config - -```sh -rsync -a /backup/romm/assets-pre-5.0/ /path/to/assets/ -rsync -a /backup/romm/config-pre-5.0/ /path/to/config/ -``` - -### 5. Start the old version - -```sh -docker compose up -d -``` - -Open a bug report on [GitHub](https://github.com/rommapp/romm/issues) with: - -- Your previous version. -- Whatever you saw in `docker logs romm` during the 5.0 startup. -- Your deployment (Docker Compose, Unraid, K8s, etc.). - -## Common upgrade issues - -### Migrations hang - -Watch `docker logs -f romm`: if Alembic is still running, it's still running, and large DBs take a while. If it's been more than 10 minutes with no progress, something's wrong. Check for: - -- DB connection issues (`docker logs romm-db`). -- Out-of-memory kills (`dmesg | grep -i oom`). -- Missing new env vars that migrations rely on. - -### WebSockets fail after upgrade - -If you're behind a reverse proxy, the upgrade shouldn't change anything, but it's a good moment to verify the proxy forwards `Upgrade: websocket` properly. See [Reverse Proxy](../install/reverse-proxy.md). - -### OIDC users lose role, come back as Viewer - -5.0 introduces `OIDC_CLAIM_ROLES`. Before 5.0, OIDC users were always Viewer. After 5.0, if you set `OIDC_CLAIM_ROLES`, roles are reassigned on **every login** based on IdP claims. If your IdP doesn't emit the expected claim yet, every OIDC user gets demoted to Viewer. - -Fix: either don't set `OIDC_CLAIM_ROLES` (keeps 4.x behaviour), or wire the claim on the IdP side before enabling the mapping. - -See [OIDC Setup → Role mapping](../administration/oidc/index.md#role-mapping-50). - -### Scheduled tasks stop firing - -5.0 renamed several cron env vars, so check [Environment Variables](../reference/environment-variables.md) and update your env to the new names: otherwise the old cron expressions are ignored and tasks run on defaults (or not at all, if you previously disabled them). - -## After the upgrade - -Once everything's stable: - -- Pin your image to `rommapp/romm:5.0.0` in version control. -- Update your uptime monitor's heartbeat endpoint: still `/api/heartbeat`, but now exposes more info (see [Observability](../administration/observability.md)). -- Take a fresh backup of the post-migration state. You want a known-good 5.0 snapshot, not just the pre-5.0 one. -- Explore the new stuff in [What's New in 5.0](../getting-started/what-is-new-in-5.md). From b22c858af465ab4ce7ebfaf09b1ab3f814b99aa8 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 13:48:15 -0400 Subject: [PATCH 038/121] docs: call the embedded store Valkey, not Redis The in-container data store shipped with the full image is Valkey. Rename references that describe the embedded/in-container store. Left as Redis: env var names (REDIS_HOST etc.), volume names (romm_redis_data, /redis-data), library names (socket.io-redis, RQ = Redis Queue), and the glossary entry that defines Valkey as a Redis fork. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/administration/observability.md | 2 +- docs/administration/scheduled-tasks.md | 4 ++-- docs/administration/server-stats.md | 2 +- docs/developers/architecture.md | 12 ++++++------ docs/developers/development-setup.md | 2 +- docs/install/truenas.md | 4 ++-- docs/reference/ports-and-endpoints.md | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/administration/observability.md b/docs/administration/observability.md index 0a88761e..79e777ca 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -63,7 +63,7 @@ Returns: - Scheduled-task schedule summary. - Watcher status. -Wire this to your uptime monitor. A failure here is real: the process is down or the DB/Redis is unreachable. +Wire this to your uptime monitor. A failure here is real: the process is down or the DB/Valkey is unreachable. ```bash # Basic uptime check diff --git a/docs/administration/scheduled-tasks.md b/docs/administration/scheduled-tasks.md index 6e0d4e2b..ccfe0617 100644 --- a/docs/administration/scheduled-tasks.md +++ b/docs/administration/scheduled-tasks.md @@ -71,9 +71,9 @@ Authorization: Bearer Full details in the [API Reference](../developers/api-reference.md). Useful for cron runs driven from outside RomM (e.g. an Ansible playbook). -### Via Redis Queue directly +### Via RQ directly -Advanced. Connect to Redis, inspect the `rq` queues, enqueue a job manually. Only do this if you know what you're doing and have a reason the API doesn't serve. +Advanced. Connect to Valkey, inspect the `rq` queues, enqueue a job manually. Only do this if you know what you're doing and have a reason the API doesn't serve. ## What each task does diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 64c68ace..59d23d7d 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -48,7 +48,7 @@ Useful for: "which platform is eating my disk?" and "which platform has the wors ## What the numbers don't include - **Disk usage for the database itself**: MariaDB / Postgres data volume isn't reported here. Use `docker system df -v` or your volume backend's native tooling. -- **Redis memory**: same. Monitor Redis separately if you're tight on RAM. +- **Valkey memory**: same. Monitor Valkey separately if you're tight on RAM. - **Per-user storage breakdown**: not exposed in 5.0. ## Using stats for capacity planning diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index 40c37a66..898ba1ff 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -57,7 +57,7 @@ A running RomM container hosts several cooperating processes: │ └──────┘ │ │ │ │ ┌────────┐ ┌────────┐ │ -│ │ RQ │←──────────→│ Redis │ │ +│ │ RQ │←──────────→│ Valkey │ │ │ │ workers│ tasks │ (embedded or external) │ │ └────────┘ └────────┘ │ └─────────────────────────────────────────────────────────┘ @@ -67,9 +67,9 @@ A running RomM container hosts several cooperating processes: - **nginx:** listens on `8080`. Serves static assets (the SPA bundle, EmulatorJS core files, Ruffle, cover images). Proxies API + WebSocket traffic to gunicorn. Streams large downloads via `mod_zip`. - **gunicorn:** the Python WSGI server, running the FastAPI app. Multiple worker processes. `WEB_SERVER_CONCURRENCY` tunes count. -- **FastAPI backend:** routes, handlers, DB access via SQLAlchemy, Redis for sessions + cache + queue. -- **RQ workers:** separate process(es) that pop jobs off Redis queues and run them. Scans, metadata syncs, cleanup tasks. -- **Redis / Valkey:** in-container by default (full image), externalisable. +- **FastAPI backend:** routes, handlers, DB access via SQLAlchemy, Valkey for sessions + cache + queue. +- **RQ workers:** separate process(es) that pop jobs off Valkey queues and run them. Scans, metadata syncs, cleanup tasks. +- **Valkey:** in-container by default (full image), externalisable (Redis-compatible). - **MariaDB / Postgres / MySQL / SQLite:** always external (or a separate container). ## Request lifecycle @@ -135,11 +135,11 @@ RQ has three priority queues: - **default:** scheduled nightlies, sync operations. - **low:** cleanup, image conversion. Run when the system's idle. -All queues share the same worker pool. Jobs are Redis-backed, so restarting RomM doesn't lose in-flight work (appendonly needs to be on, see [Redis or Valkey](../install/redis-or-valkey.md)). +All queues share the same worker pool. Jobs are persisted to Valkey, so restarting RomM doesn't lose in-flight work (appendonly needs to be on, see [Redis or Valkey](../install/redis-or-valkey.md)). ## Auth -Session state in Redis. Passwords bcrypt-hashed in the DB. JWT for OAuth2 bearer tokens. +Session state in Valkey. Passwords bcrypt-hashed in the DB. JWT for OAuth2 bearer tokens. OIDC handled via `authlib` on the backend. Session cookies issued after successful OIDC callback. From there, it's a regular session. diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index 9fa5743a..f75fb1a8 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -107,7 +107,7 @@ uv sync --all-extras --dev ### 4. Start supporting services -MariaDB and Redis come up via the dev compose file: +MariaDB and Valkey come up via the dev compose file: ```sh docker compose up -d diff --git a/docs/install/truenas.md b/docs/install/truenas.md index 4fab0cda..6c205a29 100644 --- a/docs/install/truenas.md +++ b/docs/install/truenas.md @@ -34,7 +34,7 @@ Step through the installation UI. You'll be asked for the same set of env vars a ### 3. Install -Save. TrueNAS provisions the container + DB + Redis, runs migrations, and exposes the web UI on the port you configured. If it won't boot, jump to [Troubleshooting](#troubleshooting). +Save. TrueNAS provisions the container + DB + Valkey, runs migrations, and exposes the web UI on the port you configured. If it won't boot, jump to [Troubleshooting](#troubleshooting). ## Option B: Install via YAML @@ -71,7 +71,7 @@ Save. Same troubleshooting applies, see below. If you're seeing permission errors on paths *inside* the RomM container (not on TrueNAS datasets), try temporarily running the container as root (`user: 0`) to unblock yourself, fix the offending permissions via shell, and switch back to a non-root user. -In at least one reported setup, creating a user/group in TrueNAS with UID/GID `1000:1000` and the auxiliary `apps` group was needed to get RomM talking to its embedded Redis cleanly. +In at least one reported setup, creating a user/group in TrueNAS with UID/GID `1000:1000` and the auxiliary `apps` group was needed to get RomM talking to its embedded Valkey cleanly. ### Other issues diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index 35b0f443..ea69120d 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -13,7 +13,7 @@ Quick reference for the ports + URL paths a RomM instance exposes. | --- | --- | --- | --- | | `8080` | HTTP | nginx: the front door. Serves everything. | Yes, publish this. | | `5000` | HTTP | gunicorn: FastAPI backend. Internal only. | No. nginx proxies here. | -| `6379` | TCP | Redis. Internal only (unless you externalise). | No. | +| `6379` | TCP | Valkey. Internal only (unless you externalise). | No. | Only **`8080`** should be reachable from outside the container in production. Typical compose `ports:`: @@ -90,7 +90,7 @@ Inside the container: | `/romm/assets` | User uploads (saves, states, screenshots). | **Critical.** | | `/romm/resources` | Provider-fetched cover art, screenshots. | Low priority (rebuildable). | | `/romm/config` | `config.yml`. | **Critical.** | -| `/redis-data` | Redis persistence for the in-container Redis. | Low priority. | +| `/redis-data` | Persistence for the in-container Valkey. | Low priority. | See [Install & Deploy → Docker Compose](../install/docker-compose.md) for the full volume spec. From c0369de7cdc8f6d7af1387d1e1cb010e3b0f213f Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 13:52:41 -0400 Subject: [PATCH 039/121] run fmt --- docs/about/license.md | 18 +- docs/administration/administration-page.md | 54 +- docs/administration/authentication.md | 35 +- docs/administration/firmware-management.md | 15 +- docs/administration/index.md | 8 +- .../invitations-and-registration.md | 23 +- docs/administration/metadata-providers.md | 91 +- docs/administration/observability.md | 16 +- docs/administration/oidc/authelia.md | 72 +- docs/administration/oidc/authentik.md | 15 +- docs/administration/oidc/index.md | 32 +- docs/administration/oidc/keycloak.md | 25 +- docs/administration/oidc/pocketid.md | 14 +- docs/administration/oidc/zitadel.md | 14 +- docs/administration/scanning-and-watcher.md | 78 +- docs/administration/server-stats.md | 35 +- docs/administration/ssh-sync.md | 11 +- docs/administration/users-and-roles.md | 52 +- docs/developers/api-authentication.md | 32 +- docs/developers/contributing.md | 2 + docs/developers/development-setup.md | 15 +- docs/developers/i18n.md | 16 +- docs/developers/index.md | 18 +- docs/developers/releasing.md | 2 +- docs/developers/websockets.md | 59 +- docs/ecosystem/client-api-tokens.md | 12 +- docs/ecosystem/device-sync-protocol.md | 82 +- docs/ecosystem/igir.md | 8 +- docs/ecosystem/muos-app.md | 1 + docs/ecosystem/pkgj.md | 10 +- docs/ecosystem/playnite-plugin.md | 17 +- docs/ecosystem/tinfoil.md | 1 + docs/getting-started/folder-structure.md | 2 + docs/getting-started/quick-start.md | 25 +- docs/index.md | 24 +- docs/install/backup-and-restore.md | 21 +- docs/install/databases.md | 150 +-- docs/install/docker-compose.md | 48 +- docs/install/image-variants.md | 29 +- docs/install/index.md | 14 +- docs/install/kubernetes.md | 323 +++--- docs/install/redis-or-valkey.md | 70 +- docs/install/reverse-proxy.md | 44 +- docs/install/synology.md | 5 +- docs/install/truenas.md | 6 +- docs/install/unraid.md | 3 + docs/platforms/custom-platforms.md | 28 +- docs/platforms/emulatorjs-config.md | 123 +-- docs/platforms/firmware-by-platform.md | 35 +- docs/platforms/ms-dos.md | 4 + docs/platforms/ruffle-config.md | 8 +- docs/reference/configuration-file.md | 238 ++--- docs/reference/environment-variables.md | 26 +- docs/reference/exports.md | 16 +- docs/reference/feeds.md | 30 +- docs/reference/ports-and-endpoints.md | 108 +- docs/releases/changelog.md | 3 - docs/releases/index.md | 26 +- docs/resources/snippets/env-vars.md | 282 +++--- docs/resources/snippets/scheduled-tasks.md | 26 +- .../resources/snippets/supported-platforms.md | 920 +++++++++--------- docs/troubleshooting/authentication.md | 2 +- docs/troubleshooting/in-browser-play.md | 14 +- docs/troubleshooting/kubernetes.md | 57 +- docs/troubleshooting/miscellaneous.md | 2 +- docs/troubleshooting/scanning.md | 8 +- docs/troubleshooting/synology.md | 3 +- docs/using/account-and-profile.md | 21 +- docs/using/collections.md | 14 +- docs/using/console-mode.md | 37 +- docs/using/downloads.md | 5 +- docs/using/in-browser-play.md | 52 +- docs/using/languages.md | 36 +- docs/using/library.md | 12 +- docs/using/mobile-and-tv.md | 17 +- docs/using/netplay.md | 27 +- docs/using/retroachievements.md | 2 +- docs/using/rom-patcher.md | 30 +- docs/using/saves-and-states.md | 18 +- docs/using/smart-collections.md | 18 +- docs/using/uploads.md | 30 +- docs/using/virtual-collections.md | 26 +- 82 files changed, 2013 insertions(+), 1938 deletions(-) diff --git a/docs/about/license.md b/docs/about/license.md index 7faeb825..de16ac93 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -13,7 +13,7 @@ In short, AGPL says: - **You can** use RomM privately, commercially, or at any scale. - **You can** modify the source code however you want. -- **You must** make your modifications available under AGPL-3.0 if you distribute them *or* run them as a network-accessible service for others. +- **You must** make your modifications available under AGPL-3.0 if you distribute them _or_ run them as a network-accessible service for others. - **You must** preserve copyright and license notices. The "network service" clause is the key AGPL twist: if you host a modified RomM and other people use it over the network, you owe them the source. This prevents the "hosted SaaS fork without upstream contributions" failure mode that's common with plain GPL. @@ -26,14 +26,14 @@ Full license text: [rommapp/romm/blob/master/LICENSE](https://github.com/rommapp The RomM umbrella hosts several projects under different licenses: -| Project | License | -| --- | --- | -| [rommapp/romm](https://github.com/rommapp/romm) | AGPL-3.0 | -| [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher) | GPL-3.0 | -| [rommapp/grout](https://github.com/rommapp/grout) | MIT | -| [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin) | GPL-3.0 | -| [rommapp/muos-app](https://github.com/rommapp/muos-app) | check repo | -| [rommapp/docs](https://github.com/rommapp/docs) (what you're reading) | CC0 | +| Project | License | +| --------------------------------------------------------------------- | ---------- | +| [rommapp/romm](https://github.com/rommapp/romm) | AGPL-3.0 | +| [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher) | GPL-3.0 | +| [rommapp/grout](https://github.com/rommapp/grout) | MIT | +| [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin) | GPL-3.0 | +| [rommapp/muos-app](https://github.com/rommapp/muos-app) | check repo | +| [rommapp/docs](https://github.com/rommapp/docs) (what you're reading) | CC0 | Companion repos use more permissive licenses (GPL-3.0 or MIT) because they're smaller, more-replaceable, and don't host the library. The AGPL network-service clause doesn't offer the same protection benefits there. diff --git a/docs/administration/administration-page.md b/docs/administration/administration-page.md index c982cfd7..d927e94f 100644 --- a/docs/administration/administration-page.md +++ b/docs/administration/administration-page.md @@ -11,16 +11,16 @@ This page is a map of what's behind each link. The deep mechanics of each featur ## The drawer -| Link | Who sees it | What's there | -| --- | --- | --- | -| **Profile** | Everyone | Change own username, email, password, avatar. Link a RetroAchievements account and sync achievements. | -| **User Interface** | Everyone | Locale, theme (dark/light/auto), game card layout, home dashboard ribbons, collection display settings. | -| **Library Management** | Editors + Admins | Platform bindings & version mappings, missing-ROMs tool, library folder settings. | -| **Metadata Sources** | Admins | Credentials for the 13 metadata providers, scan priority. | -| **Administration** | Admins | Users, Client API Tokens, Tasks. The main admin hub. | -| **Client API Tokens** | Everyone (own tokens) | Each user's personal API tokens. Admins see a separate "all tokens" view under Administration. | -| **Server Stats** | Admins | Numbers: platforms, games, saves, states, screenshots, disk usage. | -| **About** | Everyone | RomM version, links to Discord / GitHub / docs. | +| Link | Who sees it | What's there | +| ---------------------- | --------------------- | ------------------------------------------------------------------------------------------------------- | +| **Profile** | Everyone | Change own username, email, password, avatar. Link a RetroAchievements account and sync achievements. | +| **User Interface** | Everyone | Locale, theme (dark/light/auto), game card layout, home dashboard ribbons, collection display settings. | +| **Library Management** | Editors + Admins | Platform bindings & version mappings, missing-ROMs tool, library folder settings. | +| **Metadata Sources** | Admins | Credentials for the 13 metadata providers, scan priority. | +| **Administration** | Admins | Users, Client API Tokens, Tasks. The main admin hub. | +| **Client API Tokens** | Everyone (own tokens) | Each user's personal API tokens. Admins see a separate "all tokens" view under Administration. | +| **Server Stats** | Admins | Numbers: platforms, games, saves, states, screenshots, disk usage. | +| **About** | Everyone | RomM version, links to Discord / GitHub / docs. | ## Profile @@ -94,26 +94,26 @@ Full details in [Server Stats](server-stats.md). A few useful ones wherever the drawer is open: -| Key | Action | -| --- | --- | -| `Esc` | Close the drawer. | -| `g h` | Go home. | -| `g s` | Open the Search page. | +| Key | Action | +| ----- | ------------------------------------- | +| `Esc` | Close the drawer. | +| `g h` | Go home. | +| `g s` | Open the Search page. | | `g c` | Focus the Scan button in the sidebar. | Full shortcut reference is on the [Using RomM](../using/index.md) page. ## Role-based visibility cheat sheet -| Section | Viewer | Editor | Admin | -| --- | :---: | :---: | :---: | -| Profile | ✓ | ✓ | ✓ | -| User Interface | ✓ | ✓ | ✓ | -| Client API Tokens (own) | ✓ | ✓ | ✓ | -| About | ✓ | ✓ | ✓ | -| Library Management | - | ✓ | ✓ | -| Metadata Sources | - | - | ✓ | -| Administration → Users | - | - | ✓ | -| Administration → Tokens (all) | - | - | ✓ | -| Administration → Tasks | - | - | ✓ | -| Server Stats | - | - | ✓ | +| Section | Viewer | Editor | Admin | +| ----------------------------- | :----: | :----: | :---: | +| Profile | ✓ | ✓ | ✓ | +| User Interface | ✓ | ✓ | ✓ | +| Client API Tokens (own) | ✓ | ✓ | ✓ | +| About | ✓ | ✓ | ✓ | +| Library Management | - | ✓ | ✓ | +| Metadata Sources | - | - | ✓ | +| Administration → Users | - | - | ✓ | +| Administration → Tokens (all) | - | - | ✓ | +| Administration → Tasks | - | - | ✓ | +| Server Stats | - | - | ✓ | diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index af9e8a18..6f090427 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -19,14 +19,14 @@ Authentication flows RomM supports: Sessions are cookie-based and stored in Redis. Relevant env vars: -| Variable | Default | What it controls | -| --- | --- | --- | -| `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually**, because it invalidates every active session and every outstanding invite link. | -| `ROMM_AUTH_SECRET_KEY_FILE` | _unset_ | Alternative: read the secret from a file. Useful with Docker secrets. | -| `SESSION_MAX_AGE_SECONDS` | 86400 (24 h) | How long a session cookie lives before the user has to sign in again. | -| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | 900 (15 min) | Short-lived OAuth2 access token TTL. | -| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | 2592000 (30 d) | Refresh token TTL for OAuth2. | -| `DISABLE_CSRF_PROTECTION` | `false` | Disable CSRF middleware. Only do this behind a trusted reverse proxy that strips unwanted cross-origin traffic. | +| Variable | Default | What it controls | +| ------------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually**, because it invalidates every active session and every outstanding invite link. | +| `ROMM_AUTH_SECRET_KEY_FILE` | _unset_ | Alternative: read the secret from a file. Useful with Docker secrets. | +| `SESSION_MAX_AGE_SECONDS` | 86400 (24 h) | How long a session cookie lives before the user has to sign in again. | +| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | 900 (15 min) | Short-lived OAuth2 access token TTL. | +| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | 2592000 (30 d) | Refresh token TTL for OAuth2. | +| `DISABLE_CSRF_PROTECTION` | `false` | Disable CSRF middleware. Only do this behind a trusted reverse proxy that strips unwanted cross-origin traffic. | ## Local (username + password) @@ -36,9 +36,10 @@ The default: accounts are created via [invitations, registration, or the Setup W ```yaml environment: - - DISABLE_USERPASS_LOGIN=true + - DISABLE_USERPASS_LOGIN=true ``` + !!! warning "Keep a way in" Before setting `DISABLE_USERPASS_LOGIN=true`, confirm that at least one Admin account can sign in via OIDC and reach the Administration page. If OIDC breaks and you've already disabled local login, your only way in is editing the container env. @@ -60,12 +61,12 @@ See [OIDC Setup](oidc/index.md) for the full walkthrough. One-liner config sketc ```yaml environment: - - OIDC_ENABLED=true - - OIDC_PROVIDER=keycloak - - OIDC_CLIENT_ID=... - - OIDC_CLIENT_SECRET=... - - OIDC_SERVER_APPLICATION_URL=https://auth.example.com - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_ENABLED=true + - OIDC_PROVIDER=keycloak + - OIDC_CLIENT_ID=... + - OIDC_CLIENT_SECRET=... + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid ``` When OIDC is configured, the login page grows an "OIDC" button. Set `OIDC_AUTOLOGIN=true` to redirect straight to the IdP without the user having to click it. @@ -89,7 +90,7 @@ Turns every GET endpoint into unauthenticated read-only access: anyone reaching ```yaml environment: - - KIOSK_MODE=true + - KIOSK_MODE=true ``` Appropriate for: @@ -104,7 +105,7 @@ Authenticated users (when you do sign in) still see their full role. Kiosk only ```yaml environment: - - DISABLE_DOWNLOAD_ENDPOINT_AUTH=true + - DISABLE_DOWNLOAD_ENDPOINT_AUTH=true ``` Skips auth on `GET /api/roms/{id}/content/…` and the firmware download endpoint. Exists so third-party apps that can't carry a bearer header (dumb emulators loading a ROM by URL) can still pull files. diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index 39b77eff..f13f7fb4 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -41,13 +41,13 @@ Every emulator has its own requirements: which files it needs, specific hashes, Common examples: -| Platform | Typical firmware file | Where to put it | -| --- | --- | --- | -| PlayStation (PSX) | `scph1001.bin`, `scph5501.bin`, `scph5502.bin` | `bios/ps/` | -| Game Boy Advance | `gba_bios.bin` | `bios/gba/` | -| Sega CD | `bios_CD_U.bin`, `bios_CD_E.bin`, `bios_CD_J.bin` | `bios/segacd/` | -| Saturn | `saturn_bios.bin`, `mpr-17933.bin` | `bios/saturn/` | -| Nintendo DS | `firmware.bin`, `bios9.bin`, `bios7.bin` | `bios/nds/` | +| Platform | Typical firmware file | Where to put it | +| ----------------- | ------------------------------------------------- | --------------- | +| PlayStation (PSX) | `scph1001.bin`, `scph5501.bin`, `scph5502.bin` | `bios/ps/` | +| Game Boy Advance | `gba_bios.bin` | `bios/gba/` | +| Sega CD | `bios_CD_U.bin`, `bios_CD_E.bin`, `bios_CD_J.bin` | `bios/segacd/` | +| Saturn | `saturn_bios.bin`, `mpr-17933.bin` | `bios/saturn/` | +| Nintendo DS | `firmware.bin`, `bios9.bin`, `bios7.bin` | `bios/nds/` | File naming matters: emulators look for specific filenames. Double-check against the emulator core's documentation if something won't boot. @@ -59,6 +59,7 @@ File naming matters: emulators look for specific filenames. Double-check against - Download a firmware file (useful for verifying integrity off-host). - Delete a firmware entry. + !!! warning "Deleting firmware" Deleting from the UI **does** remove the file from disk. If you want to keep the file but unlink it from RomM, move it out of the `bios/` folder first, then rescan. diff --git a/docs/administration/index.md b/docs/administration/index.md index 631913ee..031d9bc0 100644 --- a/docs/administration/index.md +++ b/docs/administration/index.md @@ -45,11 +45,11 @@ The end-user equivalent (how to actually play the games, build collections, uplo Three built-in roles, all backed by the same scope system: -| Role | Summary | -| --- | --- | -| **Admin** | Full control. User management, task execution, every scope. First user always gets this. | +| Role | Summary | +| ---------- | ------------------------------------------------------------------------------------------- | +| **Admin** | Full control. User management, task execution, every scope. First user always gets this. | | **Editor** | Curate the library: edit ROMs, platforms, collections, upload firmware. No user management. | -| **Viewer** | Play games, manage own saves/states/profile. Read-only everywhere else. | +| **Viewer** | Play games, manage own saves/states/profile. Read-only everywhere else. | Full scope matrix in [Users & Roles](users-and-roles.md). diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md index 0c88cc99..20c917a1 100644 --- a/docs/administration/invitations-and-registration.md +++ b/docs/administration/invitations-and-registration.md @@ -20,7 +20,7 @@ To skip the wizard (e.g. when provisioning via automation and you'll create user ```yaml environment: - - DISABLE_SETUP_WIZARD=true + - DISABLE_SETUP_WIZARD=true ``` You'll then need to create the first admin via the API or by injecting a row at deploy time, because the UI won't offer a setup flow. @@ -35,12 +35,13 @@ The recommended way to add users, because it avoids you ever touching their pass Invite tokens are **single-use** and **time-limited**. Defaults: -| Setting | Default | Env var | -| --- | --- | --- | -| Expiry | 30 days | `INVITE_TOKEN_DAYS` | +| Setting | Default | Env var | +| ------- | ------- | ------------------- | +| Expiry | 30 days | `INVITE_TOKEN_DAYS` | Expired links return a clear error on the `/register` page. Generate a new one from the Users panel. + !!! tip "Invitations over HTTPS" Invite URLs include a signed token, so they're not useful to anyone without RomM's `ROMM_AUTH_SECRET_KEY`. Still, send them over a trusted channel, because once someone has a valid invite URL, they can claim the account. @@ -50,7 +51,7 @@ Off by default. To let anyone with the `/register` URL create a Viewer account w ```yaml environment: - - ALLOW_PUBLIC_REGISTRATION=true + - ALLOW_PUBLIC_REGISTRATION=true ``` When on, the login page grows a "Register" link and `/register` becomes an open endpoint. @@ -66,12 +67,12 @@ Anyone who signs up this way is a Viewer. Promote them manually from **Administr ## Role assignment at sign-up -| Sign-up method | Role assigned | -| --- | --- | -| First-user Setup Wizard | Admin (always) | -| Invite link | Whatever role the admin picked when generating the link | -| Public registration (`ALLOW_PUBLIC_REGISTRATION=true`) | Viewer | -| OIDC first login | Default Viewer, or mapped from claims via `OIDC_CLAIM_ROLES` | +| Sign-up method | Role assigned | +| ------------------------------------------------------ | ------------------------------------------------------------ | +| First-user Setup Wizard | Admin (always) | +| Invite link | Whatever role the admin picked when generating the link | +| Public registration (`ALLOW_PUBLIC_REGISTRATION=true`) | Viewer | +| OIDC first login | Default Viewer, or mapped from claims via `OIDC_CLAIM_ROLES` | Changing a user's role afterwards is a normal admin action. See [Users & Roles](users-and-roles.md). diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md index 35efac43..0ad17e1f 100644 --- a/docs/administration/metadata-providers.md +++ b/docs/administration/metadata-providers.md @@ -59,8 +59,8 @@ Access requires a Twitch account and a phone number for 2FA. Up-to-date instruct Set `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` from the values Twitch generates. ??? info "Screenshots" - ![IGDB Creation](../resources/metadata_providers/1-igdb.png) - ![IGDB Secret](../resources/metadata_providers/2-igdb.png) +![IGDB Creation](../resources/metadata_providers/1-igdb.png) +![IGDB Secret](../resources/metadata_providers/2-igdb.png) ### ScreenScraper @@ -72,6 +72,7 @@ Set `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` from the values Twitch generates. Metadata, cover art, and screenshots. [Create an account](https://www.mobygames.com/user/register/), visit your profile, and follow the **API** link to request a key. Set `MOBYGAMES_API_KEY`. + !!! important "MobyGames API is paid" Access to the MobyGames API is a [paid, non-commercial-licensed feature](https://www.mobygames.com/info/api/#non-commercial). RomM will continue to support it, but we recommend ScreenScraper as a free alternative. @@ -90,8 +91,8 @@ Each RomM user also links their own RA username in their profile to sync persona The RA database is cached locally. Refresh frequency is controlled by `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` (default: 30). ??? info "Screenshots" - ![RA API key](../resources/metadata_providers/1-ra.png) - ![RA details](../resources/metadata_providers/2-ra.png) +![RA API key](../resources/metadata_providers/1-ra.png) +![RA details](../resources/metadata_providers/2-ra.png) ### Hasheous @@ -107,9 +108,9 @@ The [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) is a communit ```yaml environment: - - LAUNCHBOX_API_ENABLED=true - - ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=true - - SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON=0 5 * * * # default: 5am daily + - LAUNCHBOX_API_ENABLED=true + - ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=true + - SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON=0 5 * * * # default: 5am daily ``` Run at least one LaunchBox update (manually from the Scan page, or wait for the cron) before using it as a scan source, because RomM won't match against an empty local DB. @@ -137,14 +138,14 @@ Expected layout: ```yaml library/ └─ roms/ - └─ gba/ - ├─ game_1.gba - ├─ game_2.gba - ├─ gamelist.xml - ├─ 3dboxes/ - ├─ covers/ - ├─ screenshots/ - └─ ... +└─ gba/ +├─ game_1.gba +├─ game_2.gba +├─ gamelist.xml +├─ 3dboxes/ +├─ covers/ +├─ screenshots/ +└─ ... ``` #### ES-DE settings @@ -169,14 +170,14 @@ Libretro's retro core metadata is used internally for platform mapping and fallb RomM honours inline tags in ROM filenames to force a match against a specific provider ID: -| Tag | Provider | -| --- | --- | -| `(igdb-xxxx)` | [IGDB](https://www.igdb.com/) | -| `(moby-xxxx)` | [MobyGames](https://www.mobygames.com/) | -| `(ra-xxxx)` | [RetroAchievements](https://retroachievements.org/) | -| `(ssfr-xxxx)` | [ScreenScraper](https://screenscraper.fr/) | -| `(launchbox-xxxx)` | [LaunchBox](https://gamesdb.launchbox-app.com/) | -| `(hltb-xxxx)` | [HowLongToBeat](https://howlongtobeat.com/) | +| Tag | Provider | +| ------------------ | --------------------------------------------------- | +| `(igdb-xxxx)` | [IGDB](https://www.igdb.com/) | +| `(moby-xxxx)` | [MobyGames](https://www.mobygames.com/) | +| `(ra-xxxx)` | [RetroAchievements](https://retroachievements.org/) | +| `(ssfr-xxxx)` | [ScreenScraper](https://screenscraper.fr/) | +| `(launchbox-xxxx)` | [LaunchBox](https://gamesdb.launchbox-app.com/) | +| `(hltb-xxxx)` | [HowLongToBeat](https://howlongtobeat.com/) | RomM will **not** rename your files to add these. They're opt-in, and renaming would conflict with other tooling that walks the filesystem. @@ -186,28 +187,28 @@ When multiple providers return different values for the same field, the winner i ```yaml scan: - priority: - metadata: - - igdb - - moby - - ss - - ra - - launchbox - - gamelist - - hasheous - - flashpoint - - hltb - artwork: - - igdb - - moby - - ss - - ra - - launchbox - - libretro - - gamelist - - hasheous - - flashpoint - - hltb + priority: + metadata: + - igdb + - moby + - ss + - ra + - launchbox + - gamelist + - hasheous + - flashpoint + - hltb + artwork: + - igdb + - moby + - ss + - ra + - launchbox + - libretro + - gamelist + - hasheous + - flashpoint + - hltb ``` Reorder these lists to taste. For example, put `ss` first if you prefer ScreenScraper boxart, or move `hltb` up if you care about completion times more than descriptions. diff --git a/docs/administration/observability.md b/docs/administration/observability.md index 79e777ca..2e1a8e2a 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -18,9 +18,9 @@ Four ways to know what RomM is doing: ```yaml environment: - - LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR - - FORCE_COLOR=0 # 1 to force colour even when not a TTY - - NO_COLOR=1 # 1 to disable colour entirely + - LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR + - FORCE_COLOR=0 # 1 to force colour even when not a TTY + - NO_COLOR=1 # 1 to disable colour entirely ``` `INFO` is the default and sane for production. Drop to `DEBUG` only while debugging a specific issue, because RomM is chatty on DEBUG. @@ -89,7 +89,7 @@ Opt-in error tracking: nothing is sent without a DSN. ```yaml environment: - - SENTRY_DSN=https://abc123@sentry.example.com/42 + - SENTRY_DSN=https://abc123@sentry.example.com/42 ``` What's sent: @@ -108,10 +108,10 @@ Opt-in distributed tracing and metrics. Useful if you run RomM alongside other s ```yaml environment: - - OTEL_ENABLED=true - - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 - - OTEL_SERVICE_NAME=romm - - OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod + - OTEL_ENABLED=true + - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 + - OTEL_SERVICE_NAME=romm + - OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod ``` Standard [OTEL env vars](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) apply. RomM emits: diff --git a/docs/administration/oidc/authelia.md b/docs/administration/oidc/authelia.md index 77c73fac..5f694101 100644 --- a/docs/administration/oidc/authelia.md +++ b/docs/administration/oidc/authelia.md @@ -22,16 +22,16 @@ In Authelia's `configuration.yml` under `identity_providers.oidc.claims_policies ```yaml identity_providers: - oidc: - claims_policies: - with_email: - id_token: - - email - - email_verified - - groups - - alt_emails - - preferred_username - - name + oidc: + claims_policies: + with_email: + id_token: + - email + - email_verified + - groups + - alt_emails + - preferred_username + - name ``` Background on why this is needed: [Authelia claims parameter restoration](https://www.authelia.com/integration/openid-connect/openid-connect-1.0-claims/#restore-functionality-prior-to-claims-parameter). @@ -42,25 +42,25 @@ Under `identity_providers.oidc.clients`, add: ```yaml identity_providers: - oidc: - clients: - - client_id: "" # see note below - client_name: "RomM" - client_secret: "$pbkdf2-sha512$" # see note below - public: false - authorization_policy: "two_factor" # or one_factor - grant_types: - - authorization_code - redirect_uris: - - "https://romm.example.com/api/oauth/openid" - claims_policy: "with_email" # must match the policy name above - scopes: - - openid - - email - - profile - - groups - userinfo_signed_response_alg: "none" - token_endpoint_auth_method: "client_secret_basic" + oidc: + clients: + - client_id: "" # see note below + client_name: "RomM" + client_secret: "$pbkdf2-sha512$" # see note below + public: false + authorization_policy: "two_factor" # or one_factor + grant_types: + - authorization_code + redirect_uris: + - "https://romm.example.com/api/oauth/openid" + claims_policy: "with_email" # must match the policy name above + scopes: + - openid + - email + - profile + - groups + userinfo_signed_response_alg: "none" + token_endpoint_auth_method: "client_secret_basic" ``` Generating IDs and secrets: see [Authelia's FAQ](https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret). Full client schema: [Authelia clients reference](https://www.authelia.com/configuration/identity-providers/openid-connect/clients/). @@ -71,13 +71,13 @@ In the `romm` service environment: ```yaml environment: - - OIDC_ENABLED=true - - OIDC_PROVIDER=authelia - - OIDC_CLIENT_ID= - - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid - - OIDC_SERVER_APPLICATION_URL=https://auth.example.com - - ROMM_BASE_URL=https://romm.example.com + - OIDC_ENABLED=true + - OIDC_PROVIDER=authelia + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com + - ROMM_BASE_URL=https://romm.example.com ``` `OIDC_REDIRECT_URI` must match what you put in `redirect_uris` exactly: scheme, host, path, no trailing slash. diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md index b9376f96..8d881648 100644 --- a/docs/administration/oidc/authentik.md +++ b/docs/administration/oidc/authentik.md @@ -19,6 +19,7 @@ Log in as admin and open **Admin Interface**. ## 2. Create a property mapping (Authentik 2025.10+) + !!! important "Authentik 2025.10 breaking change" In version 2025.10, Authentik changed the default of `email_verified` from `true` to `false`. RomM requires a verified email, so without this property mapping, authentication silently fails. @@ -79,13 +80,13 @@ Click **Create**. ```yaml environment: - - OIDC_ENABLED=true - - OIDC_PROVIDER=authentik - - OIDC_CLIENT_ID= - - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid - - OIDC_SERVER_APPLICATION_URL=https://auth.example.com/application/o/romm - - ROMM_BASE_URL=https://romm.example.com + - OIDC_ENABLED=true + - OIDC_PROVIDER=authentik + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com/application/o/romm + - ROMM_BASE_URL=https://romm.example.com ``` Note `OIDC_SERVER_APPLICATION_URL` points at the per-application URL (`/application/o/`), not the Authentik root. diff --git a/docs/administration/oidc/index.md b/docs/administration/oidc/index.md index 0994a372..a0c33e41 100644 --- a/docs/administration/oidc/index.md +++ b/docs/administration/oidc/index.md @@ -7,6 +7,7 @@ description: Wire RomM up to an OpenID Connect provider for SSO and centralised OpenID Connect (OIDC) lets users sign in to RomM through an external identity provider: Authelia, Authentik, Keycloak, PocketID, Zitadel, Okta, Auth0, or anything standards-compliant. Benefits: single sign-on across your homelab, no RomM-specific password to manage, centralised MFA, and on 5.0 you can map OIDC groups/claims to RomM roles. + !!! note "OIDC is optional" RomM has its own user system and works fine without OIDC. Enable OIDC when you already run an IdP and want RomM to follow suit. @@ -37,13 +38,13 @@ Whichever provider you pick, set these in the `romm` service's environment: ```yaml environment: - - OIDC_ENABLED=true - - OIDC_PROVIDER= - - OIDC_CLIENT_ID= - - OIDC_CLIENT_SECRET= - - OIDC_SERVER_APPLICATION_URL=https://auth.example.com - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid - - ROMM_BASE_URL=https://romm.example.com # must match your reverse-proxy URL + - OIDC_ENABLED=true + - OIDC_PROVIDER= + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_SERVER_APPLICATION_URL=https://auth.example.com + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - ROMM_BASE_URL=https://romm.example.com # must match your reverse-proxy URL ``` `OIDC_REDIRECT_URI` must exactly match what you register at the provider: same scheme, host, path, no trailing slash. @@ -54,10 +55,10 @@ By default, new OIDC users are provisioned as **Viewers**. To let your IdP assig ```yaml environment: - - OIDC_CLAIM_ROLES=groups # which claim to read - - OIDC_ROLE_VIEWER=romm-viewer,guests # group names → Viewer - - OIDC_ROLE_EDITOR=romm-editor - - OIDC_ROLE_ADMIN=romm-admin,platform-admins + - OIDC_CLAIM_ROLES=groups # which claim to read + - OIDC_ROLE_VIEWER=romm-viewer,guests # group names → Viewer + - OIDC_ROLE_EDITOR=romm-editor + - OIDC_ROLE_ADMIN=romm-admin,platform-admins ``` On every login, RomM reads the claim named by `OIDC_CLAIM_ROLES` (often `groups`, sometimes `realm_access.roles` on Keycloak, check your provider's token output). Whichever role has a matching value wins. If nothing matches, the user stays/becomes a Viewer. @@ -70,11 +71,12 @@ Bypass the RomM login page entirely. Redirects straight to the IdP: ```yaml environment: - - OIDC_AUTOLOGIN=true + - OIDC_AUTOLOGIN=true ``` Useful when you want RomM to feel like a native part of your SSO stack. Combine with `DISABLE_USERPASS_LOGIN=true` to lock out local accounts entirely. + !!! warning "Keep one local admin" Don't set `DISABLE_USERPASS_LOGIN=true` without first confirming an admin account exists on the IdP side and can log in. If OIDC breaks and you've disabled local login, you're locked out until you fix the container env. @@ -84,8 +86,8 @@ When set, hitting "Sign out" in RomM also signs the user out at the IdP: ```yaml environment: - - OIDC_RP_INITIATED_LOGOUT=true - - OIDC_END_SESSION_ENDPOINT=https://auth.example.com/application/o/end-session/ + - OIDC_RP_INITIATED_LOGOUT=true + - OIDC_END_SESSION_ENDPOINT=https://auth.example.com/application/o/end-session/ ``` The endpoint URL is provider-specific. The per-provider guides list it. @@ -96,7 +98,7 @@ By default the local part of the email (the bit before `@`) becomes the RomM use ```yaml environment: - - OIDC_USERNAME_ATTRIBUTE=preferred_username + - OIDC_USERNAME_ATTRIBUTE=preferred_username ``` ## Important notes diff --git a/docs/administration/oidc/keycloak.md b/docs/administration/oidc/keycloak.md index 6689a36b..8baf9820 100644 --- a/docs/administration/oidc/keycloak.md +++ b/docs/administration/oidc/keycloak.md @@ -35,13 +35,13 @@ In the Admin Console, select your realm → **Clients** → **Create client**. ```yaml environment: - - OIDC_ENABLED=true - - OIDC_PROVIDER=keycloak - - OIDC_CLIENT_ID=romm - - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid - - OIDC_SERVER_APPLICATION_URL=https://keycloak.example.com/realms/ - - ROMM_BASE_URL=https://romm.example.com + - OIDC_ENABLED=true + - OIDC_PROVIDER=keycloak + - OIDC_CLIENT_ID=romm + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://keycloak.example.com/realms/ + - ROMM_BASE_URL=https://romm.example.com ``` `OIDC_SERVER_APPLICATION_URL` must include the realm (`.../realms/`), not just the Keycloak root. @@ -66,9 +66,10 @@ Force users through Keycloak: ```yaml environment: - - DISABLE_USERPASS_LOGIN=true + - DISABLE_USERPASS_LOGIN=true ``` + !!! warning Before flipping this, confirm at least one Admin account can sign in via OIDC. Otherwise a broken OIDC flow locks you out. @@ -78,10 +79,10 @@ By default, new OIDC users come in as Viewers. To map Keycloak roles/groups to R ```yaml environment: - - OIDC_CLAIM_ROLES=groups # or realm_access.roles, depending on your token - - OIDC_ROLE_VIEWER=romm-viewer - - OIDC_ROLE_EDITOR=romm-editor - - OIDC_ROLE_ADMIN=romm-admin + - OIDC_CLAIM_ROLES=groups # or realm_access.roles, depending on your token + - OIDC_ROLE_VIEWER=romm-viewer + - OIDC_ROLE_EDITOR=romm-editor + - OIDC_ROLE_ADMIN=romm-admin ``` Configure Keycloak's client to include the role/group claim in the ID token (usually via a **Group Membership** or **Realm Role** client scope mapper). Values in the claim are compared against the `OIDC_ROLE_*` env vars on every login, so demoting in Keycloak takes effect on the user's next sign-in. diff --git a/docs/administration/oidc/pocketid.md b/docs/administration/oidc/pocketid.md index 6db454e4..1ab61a88 100644 --- a/docs/administration/oidc/pocketid.md +++ b/docs/administration/oidc/pocketid.md @@ -28,13 +28,13 @@ In PocketID admin: ```yaml environment: - - OIDC_ENABLED=true - - OIDC_PROVIDER=pocket-id - - OIDC_CLIENT_ID= - - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid - - OIDC_SERVER_APPLICATION_URL=https://id.example.com - - ROMM_BASE_URL=https://romm.example.com + - OIDC_ENABLED=true + - OIDC_PROVIDER=pocket-id + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://id.example.com + - ROMM_BASE_URL=https://romm.example.com ``` `OIDC_SERVER_APPLICATION_URL` is the root URL of your PocketID instance. diff --git a/docs/administration/oidc/zitadel.md b/docs/administration/oidc/zitadel.md index a19a96af..44040aaf 100644 --- a/docs/administration/oidc/zitadel.md +++ b/docs/administration/oidc/zitadel.md @@ -55,13 +55,13 @@ Open the application's **Token Settings** tab → tick **User Info inside ID Tok ```yaml environment: - - OIDC_ENABLED=true - - OIDC_PROVIDER=zitadel - - OIDC_CLIENT_ID= - - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid - - OIDC_SERVER_APPLICATION_URL=https://zitadel.example.com - - ROMM_BASE_URL=https://romm.example.com + - OIDC_ENABLED=true + - OIDC_PROVIDER=zitadel + - OIDC_CLIENT_ID= + - OIDC_CLIENT_SECRET= + - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_SERVER_APPLICATION_URL=https://zitadel.example.com + - ROMM_BASE_URL=https://romm.example.com ``` Zitadel's OIDC discovery URL is at `/.well-known/openid-configuration`, handy for debugging. diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 205452dd..60e3d59e 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -17,14 +17,14 @@ All three share the same scan engine and the same set of **scan modes**. Every scan picks one mode. Modes differ in what they touch, so use the most-targeted mode that accomplishes what you want. -| Mode | What it does | When to use | -| --- | --- | --- | -| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set; it's fast. | -| **Quick** | Skips files that already exist in the DB, with no metadata refresh. | Default for scheduled runs and the watcher. | -| **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | -| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured); rare. | -| **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash (pre-4.4) or when you suspect file corruption. | -| **Complete** | Full rescan, recalculating hashes and re-fetching metadata for everything. | Rarely, since it takes a long time. | +| Mode | What it does | When to use | +| ----------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set; it's fast. | +| **Quick** | Skips files that already exist in the DB, with no metadata refresh. | Default for scheduled runs and the watcher. | +| **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | +| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured); rare. | +| **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash (pre-4.4) or when you suspect file corruption. | +| **Complete** | Full rescan, recalculating hashes and re-fetching metadata for everything. | Rarely, since it takes a long time. | You can further scope a scan to specific **platforms** and specific **metadata providers**, useful when only one provider has changed (e.g. just enabled Hasheous → Unmatched scan, Hasheous selected, on all platforms). @@ -44,12 +44,12 @@ A running scan survives browser refreshes, and the log streams over Socket.IO. M Configured via env vars (full table in the [Scheduled Tasks reference](../reference/scheduled-tasks.md)): -| Variable | Default | Purpose | -| --- | --- | --- | -| `SCAN_INTERVAL_CRON` | `0 0 * * *` | Cron expression for the scheduled library scan. Runs a **Quick** scan by default. | -| `SCAN_TIMEOUT_HOURS` | `1` | Hard cap: scans that exceed this are killed and logged. | -| `SCAN_WORKERS` | _auto_ | Concurrent worker processes for scanning; leave as auto unless you're tuning. | -| `SEVEN_ZIP_TIMEOUT` | _unset_ | Per-archive timeout for `.7z` extraction during scan; raise if scanning huge compressed ROM sets. | +| Variable | Default | Purpose | +| -------------------- | ----------- | ------------------------------------------------------------------------------------------------- | +| `SCAN_INTERVAL_CRON` | `0 0 * * *` | Cron expression for the scheduled library scan. Runs a **Quick** scan by default. | +| `SCAN_TIMEOUT_HOURS` | `1` | Hard cap: scans that exceed this are killed and logged. | +| `SCAN_WORKERS` | _auto_ | Concurrent worker processes for scanning; leave as auto unless you're tuning. | +| `SEVEN_ZIP_TIMEOUT` | _unset_ | Per-archive timeout for `.7z` extraction during scan; raise if scanning huge compressed ROM sets. | To disable scheduled scans entirely, either unset the cron or set it to something unreachable (`SCAN_INTERVAL_CRON=0 0 31 2 *`). @@ -59,10 +59,10 @@ The watcher tails your library folder and schedules scans in response to file ev ```yaml environment: - - WATCHER_ENABLED=true - - RESCAN_ON_FILESYSTEM_CHANGE=true - - RESCAN_ON_FILESYSTEM_CHANGE_DELAY=10 # seconds before acting on an event - - WATCH_EXTENSIONS_ONLY=false # true to ignore events on non-ROM extensions + - WATCHER_ENABLED=true + - RESCAN_ON_FILESYSTEM_CHANGE=true + - RESCAN_ON_FILESYSTEM_CHANGE_DELAY=10 # seconds before acting on an event + - WATCH_EXTENSIONS_ONLY=false # true to ignore events on non-ROM extensions ``` Behaviour: @@ -81,13 +81,13 @@ Behaviour: ### Watcher vs scheduled scan -| | Watcher | Scheduled scan | -| --- | --- | --- | -| Latency | Seconds | Up to your cron interval | -| CPU cost | Only when files change | Constant cadence | -| Works over SMB/NFS | Flakily | Reliably | -| Catches renames | Yes | Yes | -| Survives a container restart | Yes, re-arms on startup | Yes | +| | Watcher | Scheduled scan | +| ---------------------------- | ----------------------- | ------------------------ | +| Latency | Seconds | Up to your cron interval | +| CPU cost | Only when files change | Constant cadence | +| Works over SMB/NFS | Flakily | Reliably | +| Catches renames | Yes | Yes | +| Survives a container restart | Yes, re-arms on startup | Yes | Run both: the watcher handles day-to-day additions, and the scheduled scan is a safety net. @@ -97,17 +97,17 @@ Scans respect the `exclude:` tree in [`config.yml`](../reference/configuration-f ```yaml exclude: - platforms: - - steam # skip entire platform folder - roms: - single_file: - extensions: [nfo, txt, bak] # single files with these exts - names: ['*.sample.*'] # Unix glob patterns - multi_file: - names: [extras] # folder names to skip - parts: - names: [thumb.png] # files inside multi-file dirs - extensions: [nfo] + platforms: + - steam # skip entire platform folder + roms: + single_file: + extensions: [nfo, txt, bak] # single files with these exts + names: ["*.sample.*"] # Unix glob patterns + multi_file: + names: [extras] # folder names to skip + parts: + names: [thumb.png] # files inside multi-file dirs + extensions: [nfo] ``` Full schema in [Configuration File](../reference/configuration-file.md). @@ -118,9 +118,9 @@ Also in `config.yml`: ```yaml scan: - priority: - region: [us, wor, ss, eu, jp] - language: [en, fr] + priority: + region: [us, wor, ss, eu, jp] + language: [en, fr] ``` When a metadata provider returns multiple regional variants (Japanese cover, US cover, European cover…), RomM picks according to this order. Same for localised titles. diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 59d23d7d..5aaa657c 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -11,25 +11,25 @@ description: Read the Server Stats admin page and know what the numbers mean. ### Top-line counts -| Metric | What it counts | -| --- | --- | -| **Platforms** | Every platform RomM has seen at least one ROM for. Deleted platforms don't count. | -| **Games** | Total ROM entries. A multi-file game (folder with multiple files) counts as 1. | -| **Saves** | User save files across all users. | -| **States** | Emulator save states across all users. | -| **Screenshots** | User-uploaded screenshots. Provider-fetched screenshots aren't counted here. | -| **Firmware** | Uploaded BIOS / firmware files. | +| Metric | What it counts | +| --------------- | --------------------------------------------------------------------------------- | +| **Platforms** | Every platform RomM has seen at least one ROM for. Deleted platforms don't count. | +| **Games** | Total ROM entries. A multi-file game (folder with multiple files) counts as 1. | +| **Saves** | User save files across all users. | +| **States** | Emulator save states across all users. | +| **Screenshots** | User-uploaded screenshots. Provider-fetched screenshots aren't counted here. | +| **Firmware** | Uploaded BIOS / firmware files. | ### Storage footprint A breakdown of disk usage by directory: -| Bucket | Maps to | Grows when | -| --- | --- | --- | -| **Library** | `/romm/library` | You add ROMs. Controlled by you, not RomM. | +| Bucket | Maps to | Grows when | +| ------------- | ----------------- | ----------------------------------------------------------------------------------------- | +| **Library** | `/romm/library` | You add ROMs. Controlled by you, not RomM. | | **Resources** | `/romm/resources` | Scans fetch cover art, screenshots, manuals. Safe to purge, can be rebuilt from a rescan. | -| **Assets** | `/romm/assets` | Users upload saves, states, screenshots. **Back this up.** | -| **Config** | `/romm/config` | Rarely: you or admins edit `config.yml`. | +| **Assets** | `/romm/assets` | Users upload saves, states, screenshots. **Back this up.** | +| **Config** | `/romm/config` | Rarely: you or admins edit `config.yml`. | ### Per-platform breakdown @@ -42,6 +42,7 @@ Under the summary, an expandable table sorted by either ROM count or disk usage. Useful for: "which platform is eating my disk?" and "which platform has the worst match rate?" + !!! note "Per-platform stats are opt-in" Computing per-platform stats walks the whole DB and costs real time on big libraries. The main stats load fast, and per-platform expansion loads on demand. @@ -55,11 +56,11 @@ Useful for: "which platform is eating my disk?" and "which platform has the wors Rule-of-thumb sizes: -| Ratio | Typical value | -| --- | --- | +| Ratio | Typical value | +| ------------------- | ------------------------------------------------------------------------------------------------ | | Resources / Library | 2-5% on average, higher if you've enabled many metadata providers or run Image Conversion often. | -| Assets / Library | <1% unless you have lots of heavy PSP/PS3 saves. | -| DB size / Games | ~10-50 KB per ROM row, depending on metadata richness. | +| Assets / Library | <1% unless you have lots of heavy PSP/PS3 saves. | +| DB size / Games | ~10-50 KB per ROM row, depending on metadata richness. | If **Resources** is ballooning, run the [Cleanup Orphaned Resources](scheduled-tasks.md#cleanup-orphaned-resources-manual) task. If **Assets** is growing unexpectedly, a user is probably uploading save states for long-form JRPGs. That's fine, just plan for it. diff --git a/docs/administration/ssh-sync.md b/docs/administration/ssh-sync.md index bdeb557c..c4cc3320 100644 --- a/docs/administration/ssh-sync.md +++ b/docs/administration/ssh-sync.md @@ -9,6 +9,7 @@ RomM's Push-Pull Device Sync task can push saves/states to registered devices an The client side (a handheld running Grout, a SteamDeck running DeckRommSync, etc.) lives in [Integrations & Ecosystem](../ecosystem/index.md). The wire protocol (API-level sync negotiation, play-session ingest) lives in [Device Sync Protocol](../ecosystem/device-sync-protocol.md). + !!! note "Most companion apps don't need this" Argosy, Playnite, and most mobile/desktop clients sync via HTTPS + Client API Tokens. SSH sync is specifically for handhelds and similar devices where RomM pushes files to a filesystem the device exposes via SSH, with Grout on muOS / NextUI being the canonical case. @@ -36,11 +37,11 @@ This produces `~/romm-sync-key` (private) and `~/romm-sync-key.pub` (public). Ke ```yaml services: - romm: - volumes: - - /home/you/romm-sync-key:/romm/.ssh/id_rsa:ro - environment: - - SSH_PRIVATE_KEY_PATH=/romm/.ssh/id_rsa + romm: + volumes: + - /home/you/romm-sync-key:/romm/.ssh/id_rsa:ro + environment: + - SSH_PRIVATE_KEY_PATH=/romm/.ssh/id_rsa ``` Keep it read-only. RomM doesn't need to modify the key. diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index cd42778f..c6280708 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -9,11 +9,11 @@ RomM is multi-user from the start. The first user created during Setup is always ## Roles -| Role | Who it's for | Default scopes | -| --- | --- | --- | -| **Admin** | You, and anyone you fully trust. | All scopes, including user management and task execution. | -| **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | -| **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | +| Role | Who it's for | Default scopes | +| ---------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| **Admin** | You, and anyone you fully trust. | All scopes, including user management and task execution. | +| **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | +| **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | Roles are a convenience layer on top of **scopes**; see the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0, so if you need finer-grained access, use the most restrictive role and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. @@ -21,27 +21,27 @@ Roles are a convenience layer on top of **scopes**; see the scope matrix below f RomM authorisation is scope-based. Every API call and UI action maps to one or more scopes, and OAuth tokens and OIDC sessions carry a subset of them. Nineteen scopes total, grouped by resource: -| Scope | Purpose | Viewer | Editor | Admin | -| --- | --- | :---: | :---: | :---: | -| `me.read` | View own profile | ✓ | ✓ | ✓ | -| `me.write` | Edit own profile | ✓ | ✓ | ✓ | -| `roms.read` | Browse ROMs | ✓ | ✓ | ✓ | -| `roms.user.read` | View own per-ROM data (rating, playtime, notes) | ✓ | ✓ | ✓ | -| `roms.user.write` | Edit own per-ROM data | ✓ | ✓ | ✓ | -| `platforms.read` | Browse platforms | ✓ | ✓ | ✓ | -| `assets.read` | View own saves/states/screenshots | ✓ | ✓ | ✓ | -| `assets.write` | Upload saves/states/screenshots | ✓ | ✓ | ✓ | -| `collections.read` | Browse collections | ✓ | ✓ | ✓ | -| `collections.write` | Create/edit collections | - | ✓ | ✓ | -| `roms.write` | Edit ROM metadata | - | ✓ | ✓ | -| `platforms.write` | Edit/create platforms | - | ✓ | ✓ | -| `firmware.read` | List firmware | - | ✓ | ✓ | -| `firmware.write` | Upload/delete firmware | - | ✓ | ✓ | -| `devices.read` | View own paired devices | ✓ | ✓ | ✓ | -| `devices.write` | Manage own paired devices | ✓ | ✓ | ✓ | -| `users.read` | List all users | - | - | ✓ | -| `users.write` | Create/edit/delete users | - | - | ✓ | -| `tasks.run` | Trigger background tasks (scan, cleanup, etc.) | - | - | ✓ | +| Scope | Purpose | Viewer | Editor | Admin | +| ------------------- | ----------------------------------------------- | :----: | :----: | :---: | +| `me.read` | View own profile | ✓ | ✓ | ✓ | +| `me.write` | Edit own profile | ✓ | ✓ | ✓ | +| `roms.read` | Browse ROMs | ✓ | ✓ | ✓ | +| `roms.user.read` | View own per-ROM data (rating, playtime, notes) | ✓ | ✓ | ✓ | +| `roms.user.write` | Edit own per-ROM data | ✓ | ✓ | ✓ | +| `platforms.read` | Browse platforms | ✓ | ✓ | ✓ | +| `assets.read` | View own saves/states/screenshots | ✓ | ✓ | ✓ | +| `assets.write` | Upload saves/states/screenshots | ✓ | ✓ | ✓ | +| `collections.read` | Browse collections | ✓ | ✓ | ✓ | +| `collections.write` | Create/edit collections | - | ✓ | ✓ | +| `roms.write` | Edit ROM metadata | - | ✓ | ✓ | +| `platforms.write` | Edit/create platforms | - | ✓ | ✓ | +| `firmware.read` | List firmware | - | ✓ | ✓ | +| `firmware.write` | Upload/delete firmware | - | ✓ | ✓ | +| `devices.read` | View own paired devices | ✓ | ✓ | ✓ | +| `devices.write` | Manage own paired devices | ✓ | ✓ | ✓ | +| `users.read` | List all users | - | - | ✓ | +| `users.write` | Create/edit/delete users | - | - | ✓ | +| `tasks.run` | Trigger background tasks (scan, cleanup, etc.) | - | - | ✓ | ## Creating users diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index 845ec041..824176c1 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -7,13 +7,13 @@ description: How to authenticate to the RomM REST API. Session cookies, Basic, O RomM's REST API accepts four authentication modes. Pick the one that matches your client: -| Mode | Who it's for | How the credential is carried | -| --- | --- | --- | -| **Session cookie** | Browser UI | `Cookie: session=…` after `POST /api/auth/login` | -| **HTTP Basic** | Quick scripts, curl one-liners | `Authorization: Basic ` | -| **OAuth2 Bearer** | Automation, CI, third-party apps | `Authorization: Bearer ` | -| **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | -| **OIDC session** | Users who sign in via SSO | Same as session cookie, but issued after OIDC callback | +| Mode | Who it's for | How the credential is carried | +| -------------------- | -------------------------------------------------------- | ------------------------------------------------------ | +| **Session cookie** | Browser UI | `Cookie: session=…` after `POST /api/auth/login` | +| **HTTP Basic** | Quick scripts, curl one-liners | `Authorization: Basic ` | +| **OAuth2 Bearer** | Automation, CI, third-party apps | `Authorization: Bearer ` | +| **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | +| **OIDC session** | Users who sign in via SSO | Same as session cookie, but issued after OIDC callback | All of them resolve to the same scope model. See the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. @@ -72,10 +72,10 @@ grant_type=password&username=alice&password=s3cret&scope=roms.read%20roms.write ```json { - "access_token": "eyJhbGciOi...", - "token_type": "bearer", - "expires_in": 900, - "refresh_token": "eyJhbGciOi..." + "access_token": "eyJhbGciOi...", + "token_type": "bearer", + "expires_in": 900, + "refresh_token": "eyJhbGciOi..." } ``` @@ -127,11 +127,11 @@ A token that holds `users.write` also implicitly grants lesser scopes like `user ## Errors -| HTTP | Meaning | -| --- | --- | -| `401 Unauthorized` | No credential, expired credential, bad credential. | -| `403 Forbidden` | Authenticated, but the identity lacks a required scope. | -| `404 Not Found` | The resource doesn't exist, or, for privacy, the identity can't see it. | +| HTTP | Meaning | +| ------------------ | ----------------------------------------------------------------------- | +| `401 Unauthorized` | No credential, expired credential, bad credential. | +| `403 Forbidden` | Authenticated, but the identity lacks a required scope. | +| `404 Not Found` | The resource doesn't exist, or, for privacy, the identity can't see it. | When debugging a 403, check: diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index 2c9db9ac..d791b6b3 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -7,6 +7,7 @@ description: How to contribute code, docs, translations, and bug reports to RomM Thanks for considering contributing. A few ground rules up front, then the mechanics. + !!! important "Big changes: open an issue first" If you're planning a large feature or architectural change, open a GitHub issue **and** hop into the [Discord](https://discord.gg/romm) to discuss with maintainers before coding. A rejected 2,000-line PR is nobody's idea of fun. @@ -16,6 +17,7 @@ The project follows the [Contributor Covenant](https://github.com/rommapp/romm/b ## AI assistance: please disclose + !!! warning "Required disclosure" If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR, including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count, but anything more does. If PR responses are generated by an AI, disclose that too. diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index f75fb1a8..2b0ad23a 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -157,6 +157,7 @@ trunk check # report what it can't Trunk runs as a pre-commit hook automatically after install. Alternative install methods are in [the Trunk docs](https://docs.trunk.io/check/usage#install-the-cli). + !!! warning "CI blocks un-linted PRs" Trunk's check runs on every PR. If it fails, your PR can't merge. Same rules as the maintainers. @@ -184,13 +185,13 @@ uv run pytest -k scan # match by name ## Useful dev URLs -| URL | Purpose | -| --- | --- | -| `http://localhost:3000` | Main UI | -| `http://localhost:3000/api/docs` | Swagger UI: try endpoints live | -| `http://localhost:3000/api/redoc` | ReDoc-rendered API reference | -| `http://localhost:3000/openapi.json` | OpenAPI spec: source of truth for client generators | -| `http://localhost:3000/api/heartbeat` | Health + config snapshot | +| URL | Purpose | +| ------------------------------------- | --------------------------------------------------- | +| `http://localhost:3000` | Main UI | +| `http://localhost:3000/api/docs` | Swagger UI: try endpoints live | +| `http://localhost:3000/api/redoc` | ReDoc-rendered API reference | +| `http://localhost:3000/openapi.json` | OpenAPI spec: source of truth for client generators | +| `http://localhost:3000/api/heartbeat` | Health + config snapshot | ## Architecture at a glance diff --git a/docs/developers/i18n.md b/docs/developers/i18n.md index 1048e451..46316565 100644 --- a/docs/developers/i18n.md +++ b/docs/developers/i18n.md @@ -40,8 +40,8 @@ Scenario: the English UI has a string that isn't translated in your locale. Fill ```json { - "scan": "Scan", - "continue_playing": "Continue Playing" + "scan": "Scan", + "continue_playing": "Continue Playing" } ``` @@ -49,7 +49,7 @@ Scenario: the English UI has a string that isn't translated in your locale. Fill ```json { - "scan": "Analyser" + "scan": "Analyser" } ``` @@ -57,8 +57,8 @@ After your contribution: ```json { - "scan": "Analyser", - "continue_playing": "Continuer à jouer" + "scan": "Analyser", + "continue_playing": "Continuer à jouer" } ``` @@ -95,8 +95,8 @@ vue-i18n uses `{placeholder}` syntax. **Don't translate placeholder names.** ```json { - "hello_user": "Hello, {username}!", - "n_games_found": "Found {count} games" + "hello_user": "Hello, {username}!", + "n_games_found": "Found {count} games" } ``` @@ -104,7 +104,7 @@ Pluralisation: ```json { - "n_games_found": "No games found | {count} game found | {count} games found" + "n_games_found": "No games found | {count} game found | {count} games found" } ``` diff --git a/docs/developers/index.md b/docs/developers/index.md index 8c9d5a87..29c1da37 100644 --- a/docs/developers/index.md +++ b/docs/developers/index.md @@ -41,12 +41,12 @@ Looking for end-user or operator content? See [Using RomM](../using/index.md) or ## Quick orientation -| I want to… | Start here | -| --- | --- | -| Call the REST API from a script | [API Authentication](api-authentication.md) + [API Reference](api-reference.md) | -| Generate a client library | [Consuming OpenAPI](openapi.md) | -| Sync saves from a handheld | [Device Sync Protocol](../ecosystem/device-sync-protocol.md) | -| Listen to scan events | [WebSockets](websockets.md) | -| Fix a bug in RomM | [Development Setup](development-setup.md) + [Contributing](contributing.md) | -| Translate the app | [Translations (i18n)](i18n.md) | -| Understand how it's put together | [Architecture](architecture.md) | +| I want to… | Start here | +| -------------------------------- | ------------------------------------------------------------------------------- | +| Call the REST API from a script | [API Authentication](api-authentication.md) + [API Reference](api-reference.md) | +| Generate a client library | [Consuming OpenAPI](openapi.md) | +| Sync saves from a handheld | [Device Sync Protocol](../ecosystem/device-sync-protocol.md) | +| Listen to scan events | [WebSockets](websockets.md) | +| Fix a bug in RomM | [Development Setup](development-setup.md) + [Contributing](contributing.md) | +| Translate the app | [Translations (i18n)](i18n.md) | +| Understand how it's put together | [Architecture](architecture.md) | diff --git a/docs/developers/releasing.md b/docs/developers/releasing.md index 1a769384..9377c074 100644 --- a/docs/developers/releasing.md +++ b/docs/developers/releasing.md @@ -156,7 +156,7 @@ For security fixes: For major versions: 1. **Announce early.** Open a tracking issue at least a month out. Post to Discord. -2. **Document the migration.** Write the upgrade guide *before* the release, so it's ready at cut time. +2. **Document the migration.** Write the upgrade guide _before_ the release, so it's ready at cut time. 3. **Deprecate first when possible.** Ship a warning in a minor release before removing in a major. 4. **Migration scripts.** If the breaking change requires user action, ship a script in `scripts/migrate_.py` or similar. diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md index 13c00224..50c51cf5 100644 --- a/docs/developers/websockets.md +++ b/docs/developers/websockets.md @@ -7,10 +7,10 @@ description: RomM's two Socket.IO endpoints for live updates and Netplay coordin RomM uses **Socket.IO** for real-time communication. Two endpoints: -| Endpoint | Purpose | -| --- | --- | -| `/ws/socket.io` | General live updates: scan progress, task status, task completion, admin notifications. | -| `/netplay/socket.io` | Netplay session coordination: room discovery, join/leave events, session lifecycle. | +| Endpoint | Purpose | +| -------------------- | --------------------------------------------------------------------------------------- | +| `/ws/socket.io` | General live updates: scan progress, task status, task completion, admin notifications. | +| `/netplay/socket.io` | Netplay session coordination: room discovery, join/leave events, session lifecycle. | Both are Redis-backed (via `socket.io-redis`) so multi-instance RomM deployments broadcast events to every replica. @@ -26,10 +26,10 @@ Socket.IO connections inherit the HTTP session: if your `Cookie` header carries ```javascript const socket = io("https://romm.example.com", { - path: "/ws/socket.io", - auth: { - token: "rmm_..." - } + path: "/ws/socket.io", + auth: { + token: "rmm_...", + }, }); ``` @@ -43,18 +43,18 @@ Default namespace. No sub-namespacing in 5.0. ### Server → client events -| Event | Payload | When | -| --- | --- | --- | -| `scan:start` | `{ scan_id, platforms, mode }` | A scan starts. | -| `scan:log` | `{ scan_id, level, message }` | Live scan log line. | -| `scan:progress` | `{ scan_id, platform, matched, unmatched, total }` | Per-platform progress update. | -| `scan:complete` | `{ scan_id, summary }` | Scan finished. | -| `task:start` | `{ task_id, task_name }` | Any scheduled/manual task starts. | -| `task:complete` | `{ task_id, task_name, status }` | Task finished (status: success/failed). | -| `rom:created` | `{ rom_id }` | New ROM added. | -| `rom:updated` | `{ rom_id }` | Metadata or user data changed. | -| `rom:deleted` | `{ rom_id }` | ROM removed. | -| `notification` | `{ message, level }` | Admin broadcast / error message. | +| Event | Payload | When | +| --------------- | -------------------------------------------------- | --------------------------------------- | +| `scan:start` | `{ scan_id, platforms, mode }` | A scan starts. | +| `scan:log` | `{ scan_id, level, message }` | Live scan log line. | +| `scan:progress` | `{ scan_id, platform, matched, unmatched, total }` | Per-platform progress update. | +| `scan:complete` | `{ scan_id, summary }` | Scan finished. | +| `task:start` | `{ task_id, task_name }` | Any scheduled/manual task starts. | +| `task:complete` | `{ task_id, task_name, status }` | Task finished (status: success/failed). | +| `rom:created` | `{ rom_id }` | New ROM added. | +| `rom:updated` | `{ rom_id }` | Metadata or user data changed. | +| `rom:deleted` | `{ rom_id }` | ROM removed. | +| `notification` | `{ message, level }` | Admin broadcast / error message. | ### Client → server events @@ -71,19 +71,19 @@ Separate endpoint for Netplay rooms. Used by EmulatorJS's Netplay logic, rarely ### Server → client events -| Event | Payload | -| --- | --- | -| `netplay:room_created` | `{ room_id, host, rom_id, password_protected }` | -| `netplay:room_updated` | `{ room_id, players }` | -| `netplay:room_destroyed` | `{ room_id }` | +| Event | Payload | +| ------------------------ | ----------------------------------------------- | +| `netplay:room_created` | `{ room_id, host, rom_id, password_protected }` | +| `netplay:room_updated` | `{ room_id, players }` | +| `netplay:room_destroyed` | `{ room_id }` | ### Client → server events -| Event | Payload | -| --- | --- | +| Event | Payload | +| --------------------- | ------------------------------------ | | `netplay:create_room` | `{ rom_id, password?, max_players }` | -| `netplay:join_room` | `{ room_id, password? }` | -| `netplay:leave_room` | `{ room_id }` | +| `netplay:join_room` | `{ room_id, password? }` | +| `netplay:leave_room` | `{ room_id }` | Plus WebRTC signalling events (`offer`, `answer`, `ice_candidate`) that shuttle between peers for the actual game session. These are opaque to anything but EmulatorJS. @@ -110,7 +110,6 @@ Common breakages: Symptom of a broken WS: HTTP 400 responses on the upgrade, and the browser console full of `WebSocket connection failed`. See [Authentication Troubleshooting → WebSockets](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). - ## Writing a client in Python Simple example, tail scan logs: diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index 119dd8b6..5af999cf 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -100,11 +100,11 @@ Response: ```json { - "id": 42, - "name": "Grout on RG35XX", - "token": "rmm_abcdef0123456789abcdef0123456789abcdef01", - "scopes": ["roms.read", "platforms.read", "assets.read", "assets.write"], - "expires_at": null + "id": 42, + "name": "Grout on RG35XX", + "token": "rmm_abcdef0123456789abcdef0123456789abcdef01", + "scopes": ["roms.read", "platforms.read", "assets.read", "assets.write"], + "expires_at": null } ``` @@ -145,7 +145,7 @@ DELETE /api/client-tokens/{id}/admin # revoke any user's token ## Scoping tokens properly -A token can only hold scopes the owning user *also* holds. If the user is an Editor, their token can hold any Editor scope, but not `users.write` (which is Admin-only). +A token can only hold scopes the owning user _also_ holds. If the user is an Editor, their token can hold any Editor scope, but not `users.write` (which is Admin-only). Useful narrow scope-sets: diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md index 91b6fc82..1bd20441 100644 --- a/docs/ecosystem/device-sync-protocol.md +++ b/docs/ecosystem/device-sync-protocol.md @@ -25,12 +25,12 @@ Authorization: Bearer rmm_... Required scopes: -| Endpoint family | Scope | -| --- | --- | -| `/devices/*` | `devices.read`, `devices.write` | -| `/sync/*` | `assets.read`, `assets.write`, `devices.write` | -| `/play-sessions/*` | `me.read`, `me.write` (read own); `users.read` (read others') | -| `/assets/*` (save/state I/O) | `assets.read`, `assets.write` | +| Endpoint family | Scope | +| ---------------------------- | ------------------------------------------------------------- | +| `/devices/*` | `devices.read`, `devices.write` | +| `/sync/*` | `assets.read`, `assets.write`, `devices.write` | +| `/play-sessions/*` | `me.read`, `me.write` (read own); `users.read` (read others') | +| `/assets/*` (save/state I/O) | `assets.read`, `assets.write` | ## Registering a device @@ -69,11 +69,11 @@ The device remembers `id` for all subsequent calls. ### Sync modes -| Mode | Behaviour | -| --- | --- | -| `pull_only` | Server only pushes, and device-side changes are ignored. | +| Mode | Behaviour | +| ----------- | ----------------------------------------------------------- | +| `pull_only` | Server only pushes, and device-side changes are ignored. | | `push_only` | Device pushes saves up, and server changes never flow down. | -| `push_pull` | Bidirectional (most common). | +| `push_pull` | Bidirectional (most common). | ## Sync negotiation @@ -109,37 +109,37 @@ RomM returns a set of **operations** the device should execute: ```json { - "session_id": "550e8400-e29b-41d4-a716-446655440000", - "operations": [ - { - "type": "upload", - "rom_id": 1234, - "file": "mario.srm", - "destination": "/api/saves", - "reason": "server_newer" - }, - { - "type": "download", - "rom_id": 5678, - "file": "zelda.srm", - "source": "/api/saves/42/content", - "dest_path": "/saves/zelda.srm" - }, - { - "type": "conflict", - "rom_id": 9999, - "file": "tetris.srm", - "server_mtime": "...", - "device_mtime": "...", - "resolution": "keep_both" - }, - { - "type": "noop", - "rom_id": 1111, - "file": "goldeneye.srm", - "reason": "already_synced" - } - ] + "session_id": "550e8400-e29b-41d4-a716-446655440000", + "operations": [ + { + "type": "upload", + "rom_id": 1234, + "file": "mario.srm", + "destination": "/api/saves", + "reason": "server_newer" + }, + { + "type": "download", + "rom_id": 5678, + "file": "zelda.srm", + "source": "/api/saves/42/content", + "dest_path": "/saves/zelda.srm" + }, + { + "type": "conflict", + "rom_id": 9999, + "file": "tetris.srm", + "server_mtime": "...", + "device_mtime": "...", + "resolution": "keep_both" + }, + { + "type": "noop", + "rom_id": 1111, + "file": "goldeneye.srm", + "reason": "already_synced" + } + ] } ``` diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md index 780f5452..9d845873 100644 --- a/docs/ecosystem/igir.md +++ b/docs/ecosystem/igir.md @@ -5,7 +5,7 @@ description: Clean up and normalise your ROM collection with Igir before importi # Igir Collection Manager -[Igir](https://igir.io/) is a zero-setup ROM collection manager: sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se, but more a pre-processing tool. Useful for cleaning up a library *before* importing into RomM, so RomM's scans have a better-named, better-organised starting point. +[Igir](https://igir.io/) is a zero-setup ROM collection manager: sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se, but more a pre-processing tool. Useful for cleaning up a library _before_ importing into RomM, so RomM's scans have a better-named, better-organised starting point. **This is not an official RomM app.** Igir is a separate community project. We document integration here because it's a common workflow and produces a RomM-compatible layout directly. @@ -157,9 +157,9 @@ Once `roms-verified/` looks right, mount it as RomM's library: ```yaml services: - romm: - volumes: - - /path/to/roms-verified:/romm/library/roms:ro + romm: + volumes: + - /path/to/roms-verified:/romm/library/roms:ro ``` Read-only is safer: if you need Igir to re-clean, you work in the parallel `roms-unverified/` and re-promote to `roms-verified/`. diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md index 3f891cdf..a01ed593 100644 --- a/docs/ecosystem/muos-app.md +++ b/docs/ecosystem/muos-app.md @@ -44,6 +44,7 @@ Installation uses muOS's [Archive Manager](https://muos.dev/installation/archive 6. Launch from **Applications → RomM** on the device. + !!! tip "Use a dedicated account or token" The username/password lives in plaintext on the SD card. Use a dedicated Viewer-role account for the handheld, not your admin credentials. diff --git a/docs/ecosystem/pkgj.md b/docs/ecosystem/pkgj.md index 7eea6cda..d918077a 100644 --- a/docs/ecosystem/pkgj.md +++ b/docs/ecosystem/pkgj.md @@ -18,12 +18,12 @@ description: Install PS Vita and PSP games from your RomM library via pkgj homeb RomM exposes four pkgi-compatible feeds: -| Content | URL | -| --- | --- | +| Content | URL | +| ------------- | --------------------------------------- | | PS Vita games | `{romm_url}/api/feeds/pkgi/psvita/game` | -| PS Vita DLCs | `{romm_url}/api/feeds/pkgi/psvita/dlc` | -| PSP games | `{romm_url}/api/feeds/pkgi/psp/game` | -| PSP DLCs | `{romm_url}/api/feeds/pkgi/psp/dlc` | +| PS Vita DLCs | `{romm_url}/api/feeds/pkgi/psvita/dlc` | +| PSP games | `{romm_url}/api/feeds/pkgi/psp/game` | +| PSP DLCs | `{romm_url}/api/feeds/pkgi/psp/dlc` | ## Configuring pkgj diff --git a/docs/ecosystem/playnite-plugin.md b/docs/ecosystem/playnite-plugin.md index d9f130fa..ceb55153 100644 --- a/docs/ecosystem/playnite-plugin.md +++ b/docs/ecosystem/playnite-plugin.md @@ -55,6 +55,7 @@ Playnite: **Menu → Library → Configure Integrations → RomM**. - ✗ `https://romm.example.com/` - **Username + password**: Playnite stores these in plaintext. Use a **dedicated Viewer-role account** for Playnite, not your admin account. + !!! tip "Use a Client API Token instead" If your RomM version supports API tokens, create a Viewer-scoped [Client API Token](client-api-tokens.md) and use it instead of a password. Safer and easier to revoke. @@ -62,14 +63,14 @@ Playnite: **Menu → Library → Configure Integrations → RomM**. One mapping per platform: -| Field | Purpose | Example | Required | -| --- | --- | --- | :---: | -| Emulator | Which Playnite emulator to use | Dolphin | ✓ | -| Emulator Profile | The profile within the emulator | Nintendo GameCube | ✓ | -| Platform | The RomM platform slug | Nintendo GameCube | ✓ | -| Destination Path | Where downloaded ROMs are stored | `C:\roms\gc` | ✓ | -| Auto-extract | Unpack zipped ROMs after download | | | -| Enabled | Whether this mapping is active | | | +| Field | Purpose | Example | Required | +| ---------------- | --------------------------------- | ----------------- | :------: | +| Emulator | Which Playnite emulator to use | Dolphin | ✓ | +| Emulator Profile | The profile within the emulator | Nintendo GameCube | ✓ | +| Platform | The RomM platform slug | Nintendo GameCube | ✓ | +| Destination Path | Where downloaded ROMs are stored | `C:\roms\gc` | ✓ | +| Auto-extract | Unpack zipped ROMs after download | | | +| Enabled | Whether this mapping is active | | | Map every platform you want Playnite to see. Platforms without a mapping are skipped during import. diff --git a/docs/ecosystem/tinfoil.md b/docs/ecosystem/tinfoil.md index 7a836190..2dc23ce8 100644 --- a/docs/ecosystem/tinfoil.md +++ b/docs/ecosystem/tinfoil.md @@ -67,6 +67,7 @@ The bracketed `[0100000000010000]` is the title ID. Without it, Tinfoil shows th ![TitleID example](../resources/tinfoil/titleid.jpg) + !!! info "Improvement coming" 5.0 improves RomM's title-ID handling: it auto-detects title IDs from filenames that have them and feeds Tinfoil regardless of whether your filename format is standard. The guidance above still applies for older RomM releases. diff --git a/docs/getting-started/folder-structure.md b/docs/getting-started/folder-structure.md index cfdc4769..99db9244 100644 --- a/docs/getting-started/folder-structure.md +++ b/docs/getting-started/folder-structure.md @@ -38,6 +38,7 @@ The path you mount into the RomM container as `/romm/library` depends on which s See the [reference Docker Compose](../install/docker-compose.md) for where `/romm/library` lives. + !!! tip "Platform folder names" The platform folder name has to match a known slug (`gbc`, `gba`, `ps`, `snes`, etc.). Full list in [Supported Platforms](../platforms/supported-platforms.md). If your existing folder names don't match (say, `super_nintendo/` instead of `snes/`), override the mapping via `system.platforms` in [`config.yml`](../reference/configuration-file.md). @@ -149,6 +150,7 @@ Some games come as **folders** instead of single files, which could include mult
+ !!! note "Starting from scratch?" RomM can also bootstrap an empty library. If you upload files through the web UI without any existing structure, RomM creates **Structure A** on your behalf. diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index db6c5fd3..b5e2c410 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -15,6 +15,7 @@ You'll need: - Your ROM files organised in the expected [folder structure](folder-structure.md). - API credentials for at least one [metadata provider](../administration/metadata-providers.md). Hasheous + IGDB + SteamGridDB + Retroachievements is the recommended pairing. RomM will run without any provider configured, but matching quality will suffer. + !!! warning "Metadata providers are recommended" RomM works without a metadata API for basic use, but setup problems and companion-app integrations (e.g. Playnite) can fail without them. Setting up **IGDB**, **SteamGridDB**, and **Retroachievements** API keys before your first scan is strongly recommended. @@ -23,22 +24,22 @@ You'll need: Start from the reference file shipped in the RomM repo. A known-good, minimally-edited version is included below. Save it as `docker-compose.yml` in an empty directory on your host. ???+ example "docker-compose.yml" - ``` yaml +`yaml --8<-- "quick-start.docker-compose.yml" - ``` + ` You'll want to edit the following values before launching: -| Where | Variable | What to put | -| --- | --- | --- | -| `romm-db` | `MARIADB_ROOT_PASSWORD` | A long, randomly generated password. | -| `romm-db` | `MARIADB_PASSWORD` | A separate long, randomly generated password. | -| `romm` | `DB_PASSWD` | Must match `MARIADB_PASSWORD` above. | -| `romm` | `ROMM_AUTH_SECRET_KEY` | Generate with `openssl rand -hex 32` and keep it secret. | -| `romm` | Metadata provider creds | Fill in only the providers you've registered with (see [Metadata Providers](../administration/metadata-providers.md)). | -| `romm` | `/path/to/library` volume | Host path to the directory containing your `roms/` folder. | -| `romm` | `/path/to/assets` volume | Host path where RomM will store saves, states, uploaded screenshots. | -| `romm` | `/path/to/config` volume | Host path to a directory that will hold `config.yml`. | +| Where | Variable | What to put | +| --------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `romm-db` | `MARIADB_ROOT_PASSWORD` | A long, randomly generated password. | +| `romm-db` | `MARIADB_PASSWORD` | A separate long, randomly generated password. | +| `romm` | `DB_PASSWD` | Must match `MARIADB_PASSWORD` above. | +| `romm` | `ROMM_AUTH_SECRET_KEY` | Generate with `openssl rand -hex 32` and keep it secret. | +| `romm` | Metadata provider creds | Fill in only the providers you've registered with (see [Metadata Providers](../administration/metadata-providers.md)). | +| `romm` | `/path/to/library` volume | Host path to the directory containing your `roms/` folder. | +| `romm` | `/path/to/assets` volume | Host path where RomM will store saves, states, uploaded screenshots. | +| `romm` | `/path/to/config` volume | Host path to a directory that will hold `config.yml`. | Generate the auth secret now so you don't forget: diff --git a/docs/index.md b/docs/index.md index 08ceae15..4af763c1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,49 +23,49 @@ RomM (ROM Manager) lets you scan, enrich, organise, and play your game collectio
-- :material-rocket-launch-outline: __I'm new, get me running__ +- :material-rocket-launch-outline: **I'm new, get me running** - --- + *** 15 minute Docker Compose walkthrough to a working RomM instance. [Quick Start →](getting-started/quick-start.md) -- :material-upload-outline: __I'm upgrading from 4.x__ +- :material-upload-outline: **I'm upgrading from 4.x** - --- + *** Breaking changes, migrations, and the pre-flight checklist for RomM 5.0. [Upgrade guide →](releases/upgrading-to-5.0.md) -- :material-cog-outline: __I'm running RomM for my family/friends__ +- :material-cog-outline: **I'm running RomM for my family/friends** - --- + *** Users, roles, OIDC, scheduled tasks, backups, reverse-proxy recipes. [Administration →](administration/index.md) -- :material-controller-classic-outline: __I just want to play__ +- :material-controller-classic-outline: **I just want to play** - --- + *** Library, collections, saves & states, Console Mode, ROM Patcher, Netplay. [Using RomM →](using/index.md) -- :material-api: __I'm building on top of RomM__ +- :material-api: **I'm building on top of RomM** - --- + *** REST API reference, WebSockets, device sync protocol, client tokens. [Developers →](developers/index.md) -- :material-book-open-outline: __Just looking something up__ +- :material-book-open-outline: **Just looking something up** - --- + *** Environment variables, `config.yml`, scheduled tasks, glossary. diff --git a/docs/install/backup-and-restore.md b/docs/install/backup-and-restore.md index 01b545fb..072e82e7 100644 --- a/docs/install/backup-and-restore.md +++ b/docs/install/backup-and-restore.md @@ -1,25 +1,26 @@ --- title: Backup & Restore -description: Protect your RomM install against data loss, and move it between hosts. +description: Protect your RomM install against data loss and move it between hosts. --- # Backup & Restore -This page covers both routine backups and migrating RomM to a new host: same procedure, different frequency. +This page covers both routine backups and migrating RomM to a new host. ## What to back up -| Path / volume | What's in it | Backup? | -| --- | --- | --- | -| **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical**: back this up nightly. | -| **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical**: back this up nightly. | -| **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes, but small and painful to recreate. | -| `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority, and can be re-downloaded on a rescan. Including it speeds up recovery. | -| `/redis-data` | Task queue state | Low priority, in-flight tasks only, and lost tasks can be re-run. | -| **`/romm/library`** | Your ROM files | Back this up **separately**. It's your source data and you should already have a backup strategy for it independent of RomM. | +| Path / volume | What's in it | Backup? | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical**: back this up nightly. | +| **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical**: back this up nightly. | +| **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes, but small and painful to recreate. | +| `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority, and can be re-downloaded on a rescan. Including it speeds up recovery. | +| `/redis-data` | Task queue state | Low priority, in-flight tasks only, and lost tasks can be re-run. | +| **`/romm/library`** | Your ROM files | Back this up **separately**. It's your source data and you should already have a backup strategy for it independent of RomM. | ## Routine backup + !!! warning "Always stop the stack or use consistent snapshots" A live `cp -r` of the DB volume can copy an inconsistent state. Either bring the stack down briefly, or use `mysqldump` / `pg_dump` for a consistent logical dump. diff --git a/docs/install/databases.md b/docs/install/databases.md index f06c884c..8381d84b 100644 --- a/docs/install/databases.md +++ b/docs/install/databases.md @@ -7,12 +7,12 @@ description: Supported database drivers for RomM, connection strings, and recomm RomM uses SQLAlchemy + Alembic for persistence. Four drivers are supported, so pick based on what you already run. -| Driver | `ROMM_DB_DRIVER` | Image | Default port | Notes | -| --- | --- | --- | --- | --- | -| **MariaDB** (default, recommended) | `mariadb` | `mariadb:11` | `3306` | What the reference compose uses. Well-tested. | -| **MySQL** | `mysql` | `mysql:8` | `3306` | Largely interchangeable with MariaDB for RomM. | -| **PostgreSQL** | `postgresql` | `postgres:16` | `5432` | Supported. Use if you already run Postgres. | -| **SQLite** | `sqlite` | _(file on disk)_ | n/a | Dev/tiny deployments only. Not recommended for anything multi-user or larger than a few hundred games. | +| Driver | `ROMM_DB_DRIVER` | Image | Default port | Notes | +| ---------------------------------- | ---------------- | ---------------- | ------------ | ------------------------------------------------------------------------------------------------------ | +| **MariaDB** (default, recommended) | `mariadb` | `mariadb:11` | `3306` | What the reference compose uses. Well-tested. | +| **MySQL** | `mysql` | `mysql:8` | `3306` | Largely interchangeable with MariaDB for RomM. | +| **PostgreSQL** | `postgresql` | `postgres:16` | `5432` | Supported. Use if you already run Postgres. | +| **SQLite** | `sqlite` | _(file on disk)_ | n/a | Dev/tiny deployments only. Not recommended for anything multi-user or larger than a few hundred games. | RomM runs Alembic migrations automatically on startup. No manual step when upgrading. @@ -22,30 +22,30 @@ This is what the [reference Compose](docker-compose.md) sets up. No extra config ```yaml services: - romm: - environment: - - ROMM_DB_DRIVER=mariadb # optional; this is the default - - DB_HOST=romm-db - - DB_PORT=3306 - - DB_NAME=romm - - DB_USER=romm-user - - DB_PASSWD= - - romm-db: - image: mariadb:11 - environment: - - MARIADB_ROOT_PASSWORD= - - MARIADB_DATABASE=romm - - MARIADB_USER=romm-user - - MARIADB_PASSWORD= - volumes: - - mysql_data:/var/lib/mysql - healthcheck: - test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] - start_period: 30s - interval: 10s - timeout: 5s - retries: 5 + romm: + environment: + - ROMM_DB_DRIVER=mariadb # optional; this is the default + - DB_HOST=romm-db + - DB_PORT=3306 + - DB_NAME=romm + - DB_USER=romm-user + - DB_PASSWD= + + romm-db: + image: mariadb:11 + environment: + - MARIADB_ROOT_PASSWORD= + - MARIADB_DATABASE=romm + - MARIADB_USER=romm-user + - MARIADB_PASSWORD= + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + start_period: 30s + interval: 10s + timeout: 5s + retries: 5 ``` ## MySQL @@ -54,57 +54,57 @@ Identical compose to MariaDB, but swap the image and the healthcheck: ```yaml services: - romm: - environment: - - ROMM_DB_DRIVER=mysql - - DB_HOST=romm-db - - DB_PORT=3306 - - DB_NAME=romm - - DB_USER=romm-user - - DB_PASSWD= - - romm-db: - image: mysql:8 - environment: - - MYSQL_ROOT_PASSWORD= - - MYSQL_DATABASE=romm - - MYSQL_USER=romm-user - - MYSQL_PASSWORD= - volumes: - - mysql_data:/var/lib/mysql - healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] - interval: 10s - timeout: 5s - retries: 5 + romm: + environment: + - ROMM_DB_DRIVER=mysql + - DB_HOST=romm-db + - DB_PORT=3306 + - DB_NAME=romm + - DB_USER=romm-user + - DB_PASSWD= + + romm-db: + image: mysql:8 + environment: + - MYSQL_ROOT_PASSWORD= + - MYSQL_DATABASE=romm + - MYSQL_USER=romm-user + - MYSQL_PASSWORD= + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 ``` ## PostgreSQL ```yaml services: - romm: - environment: - - ROMM_DB_DRIVER=postgresql - - DB_HOST=romm-db - - DB_PORT=5432 - - DB_NAME=romm - - DB_USER=romm-user - - DB_PASSWD= - - romm-db: - image: postgres:16 - environment: - - POSTGRES_DB=romm - - POSTGRES_USER=romm-user - - POSTGRES_PASSWORD= - volumes: - - pg_data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U romm-user -d romm"] - interval: 10s - timeout: 5s - retries: 5 + romm: + environment: + - ROMM_DB_DRIVER=postgresql + - DB_HOST=romm-db + - DB_PORT=5432 + - DB_NAME=romm + - DB_USER=romm-user + - DB_PASSWD= + + romm-db: + image: postgres:16 + environment: + - POSTGRES_DB=romm + - POSTGRES_USER=romm-user + - POSTGRES_PASSWORD= + volumes: + - pg_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U romm-user -d romm"] + interval: 10s + timeout: 5s + retries: 5 ``` ## SQLite (not recommended) diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index f8e9f3f3..c02d30d8 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -22,21 +22,21 @@ The RomM stack has three parts: ### `romm` -| Field | Value | Notes | -| --- | --- | --- | -| `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`; pin to a specific tag (`5.0.0`) for production, and see [Image Variants](image-variants.md) for `slim` vs `full`. | -| `ports` | `80:8080` | Container listens on `8080`; expose through a reverse proxy in production (see [Reverse Proxy](reverse-proxy.md)). | -| `volumes` | see below | RomM writes to four distinct paths inside the container. | -| `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | +| Field | Value | Notes | +| ------------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`; pin to a specific tag (`5.0.0`) for production, and see [Image Variants](image-variants.md) for `slim` vs `full`. | +| `ports` | `80:8080` | Container listens on `8080`; expose through a reverse proxy in production (see [Reverse Proxy](reverse-proxy.md)). | +| `volumes` | see below | RomM writes to four distinct paths inside the container. | +| `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | #### Volumes -| Path inside container | Purpose | Backup priority | -| --- | --- | --- | -| `/romm/library` | Your ROM files, typically mounted **read-only**. | No: this is your source data, back it up separately. | -| `/romm/assets` | User uploads: saves, states, uploaded screenshots. | **Critical**, back this up with your DB. | -| `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low, and can be re-downloaded on a rescan. | -| `/romm/config` | Holds `config.yml`. | **Critical**, hand-tuned config, back it up. | +| Path inside container | Purpose | Backup priority | +| --------------------- | ------------------------------------------------------ | ---------------------------------------------------- | +| `/romm/library` | Your ROM files, typically mounted **read-only**. | No: this is your source data, back it up separately. | +| `/romm/assets` | User uploads: saves, states, uploaded screenshots. | **Critical**, back this up with your DB. | +| `/romm/resources` | Metadata assets fetched from IGDB, ScreenScraper, etc. | Low, and can be re-downloaded on a rescan. | +| `/romm/config` | Holds `config.yml`. | **Critical**, hand-tuned config, back it up. | See [Backup & Restore](backup-and-restore.md) for the full procedure. @@ -44,21 +44,21 @@ See [Backup & Restore](backup-and-restore.md) for the full procedure. Minimal set: see the [Environment Variables reference](../reference/environment-variables.md) for the complete list. -| Variable | What it does | -| --- | --- | -| `ROMM_AUTH_SECRET_KEY` | (Required) Generate the JWT signing secret with `openssl rand -hex 32`. | -| `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | -| `ROMM_DB_DRIVER` | One of `mariadb` (default), `mysql`, or `postgresql`. | -| `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWD` | Only set if you're using an external Redis/Valkey instance. | -| Metadata provider creds | See [Metadata Providers](../administration/metadata-providers.md). | +| Variable | What it does | +| -------------------------------------------- | ----------------------------------------------------------------------- | +| `ROMM_AUTH_SECRET_KEY` | (Required) Generate the JWT signing secret with `openssl rand -hex 32`. | +| `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | +| `ROMM_DB_DRIVER` | One of `mariadb` (default), `mysql`, or `postgresql`. | +| `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWD` | Only set if you're using an external Redis/Valkey instance. | +| Metadata provider creds | See [Metadata Providers](../administration/metadata-providers.md). | ### `romm-db` (MariaDB) -| Field | Value | Notes | -| --- | --- | --- | -| `image` | `mariadb:latest` | Pin to a major version (`mariadb:11`) for production. | -| `volumes` | `mysql_data:/var/lib/mysql` | Back this up, since it's your entire catalogue. | -| `healthcheck` | `healthcheck.sh --connect --innodb_initialized` | RomM waits for this. | +| Field | Value | Notes | +| ------------- | ----------------------------------------------- | ----------------------------------------------------- | +| `image` | `mariadb:latest` | Pin to a major version (`mariadb:11`) for production. | +| `volumes` | `mysql_data:/var/lib/mysql` | Back this up, since it's your entire catalogue. | +| `healthcheck` | `healthcheck.sh --connect --innodb_initialized` | RomM waits for this. | The default compose file uses MariaDB because it requires no extra driver configuration. To use PostgreSQL, swap the image for `postgres:16`, set `ROMM_DB_DRIVER=postgresql`, and point `DB_PORT` at `5432`: see [Databases](databases.md) for the full swap. diff --git a/docs/install/image-variants.md b/docs/install/image-variants.md index 0f77dd3a..2960269c 100644 --- a/docs/install/image-variants.md +++ b/docs/install/image-variants.md @@ -5,14 +5,14 @@ description: Choose between the slim and full RomM container images. # Image Variants -RomM publishes two production image variants. They're interchangeable at the config level, so pick based on whether you want in-browser emulation. +RomM publishes two production image variants. They're interchangeable at the config level, so pick based on whether you want bake-in EmulatorJS cores or fetch them from the CDN at runtime. -| Variant | Tag | Includes | Approx size | When to pick | -| --- | --- | --- | --- | --- | -| **Full** (default) | `rommapp/romm:latest`, `rommapp/romm:5.0.0` | Backend + frontend + **EmulatorJS** + **Ruffle** + embedded Redis + nginx with `mod_zip` | ~1.2 GB | You want in-browser play. 95% of users pick this. | -| **Slim** | `rommapp/romm:slim`, `rommapp/romm:5.0.0-slim` | Backend + frontend + embedded Redis + nginx with `mod_zip` | ~400 MB | Headless use (API + native-app clients only), bandwidth-constrained hosts, or when you're running emulators elsewhere. | +| Variant | Tag | Approx size | When to pick | +| ------------------ | --------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------- | +| **Full** (default) | `rommapp/romm:latest` `rommapp/romm:5.0.0` | ~400MB | You want in-browser play (most users pick this). | +| **Slim** | `rommapp/romm:slim` `rommapp/romm:5.0.0-slim` | ~100MB | Headless use (API + native-app clients only) or when you're running emulators elsewhere. | -Both variants are published on Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`). The GHCR images track the same tags and are a good choice if you rely on Docker Hub rate limits. +Both variants are published on Docker Hub (`docker.io/rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`). The GHCR images track the same tags and are a good choice if you run into Docker Hub's rate limits. ## Switching variants @@ -20,8 +20,8 @@ Change the `image:` line in `docker-compose.yml` and recreate the container: ```yaml services: - romm: - image: rommapp/romm:5.0.0-slim # was :5.0.0 + romm: + image: rommapp/romm:5.0.0-slim # was :5.0.0 ``` ```sh @@ -29,16 +29,3 @@ docker compose up -d ``` No data migration is required. Saves, states, metadata, and `config.yml` are the same across variants. - -## Dev images - -`rommapp/romm:dev-slim` and `rommapp/romm:dev-full` track the `master` branch with hot-reload-friendly entrypoints. Useful for testing changes against a real library, but not recommended for anything you care about. See [Development Setup](../developers/development-setup.md). - -## What's actually different - -The two images share the same backend code, frontend bundle, nginx config, and entrypoint. The slim image omits: - -- **EmulatorJS** (~700 MB uncompressed): the in-browser retro emulator bundle. -- **Ruffle** (~20 MB): the Flash/Shockwave emulator. - -If you set `ENABLE_EMULATORJS=false` and `ENABLE_RUFFLE=false` on the full image, behaviour matches slim, and only the image is larger. Setting them to `true` on the slim image will cause players to 404 at runtime. diff --git a/docs/install/index.md b/docs/install/index.md index 32d92d5f..12e02280 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -9,13 +9,13 @@ RomM is distributed as a Docker image. Every supported deployment runs the same ## Pick your path -| If you're on… | Start here | -| --- | --- | -| **Linux server / NAS** | [The canonical reference setup](docker-compose.md) | -| **Unraid** | [Community Apps template or DCM](unraid.md) | -| **Synology** |[Container Manager + DSM-specific notes](synology.md) | -| **TrueNAS** | [App Catalog or YAML install](truenas.md) | -| **Kubernetes** | [Manifest examples](kubernetes.md) | +| If you're on… | Start here | +| ---------------------- | ----------------------------------------------------- | +| **Linux server / NAS** | [The canonical reference setup](docker-compose.md) | +| **Unraid** | [Community Apps template or DCM](unraid.md) | +| **Synology** | [Container Manager + DSM-specific notes](synology.md) | +| **TrueNAS** | [App Catalog or YAML install](truenas.md) | +| **Kubernetes** | [Manifest examples](kubernetes.md) | If none of those match, start with [Docker Compose](docker-compose.md) and adapt. diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index 6604525a..74786191 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -7,6 +7,7 @@ description: Deploy RomM on Kubernetes with manifest examples, required quirks, There's no official Helm chart for RomM in 5.0. This page walks through a production-grade manifest set and points at the community charts worth looking at. + !!! note "Community-maintained charts" If you'd rather not hand-author manifests, there are community Helm charts around. Check [ArtifactHub](https://artifacthub.io/packages/search?ts_query_web=romm) and the `#kubernetes` channel in the [RomM Discord](https://discord.gg/P5HtHnhUDH). The RomM team doesn't publish or formally support any chart today, so pick one with active maintenance and recent releases. @@ -32,12 +33,12 @@ Fix: disable service-link env vars on the pod. apiVersion: apps/v1 kind: Deployment metadata: - name: romm - namespace: romm + name: romm + namespace: romm spec: - template: - spec: - enableServiceLinks: false # ← required + template: + spec: + enableServiceLinks: false # ← required ``` Without this, RomM crashloops on startup. This is the single most common Kubernetes gotcha. If you're here because of the error above, add the flag and move on. @@ -48,7 +49,7 @@ Without this, RomM crashloops on startup. This is the single most common Kuberne apiVersion: v1 kind: Namespace metadata: - name: romm + name: romm ``` ## Secrets @@ -57,20 +58,20 @@ metadata: apiVersion: v1 kind: Secret metadata: - name: romm-secrets - namespace: romm + name: romm-secrets + namespace: romm type: Opaque stringData: - ROMM_AUTH_SECRET_KEY: "" - DB_PASSWD: "" - MARIADB_ROOT_PASSWORD: "" - # Metadata providers: fill in only what you've configured: - IGDB_CLIENT_ID: "" - IGDB_CLIENT_SECRET: "" - SCREENSCRAPER_USER: "" - SCREENSCRAPER_PASSWORD: "" - RETROACHIEVEMENTS_API_KEY: "" - STEAMGRIDDB_API_KEY: "" + ROMM_AUTH_SECRET_KEY: "" + DB_PASSWD: "" + MARIADB_ROOT_PASSWORD: "" + # Metadata providers: fill in only what you've configured: + IGDB_CLIENT_ID: "" + IGDB_CLIENT_SECRET: "" + SCREENSCRAPER_USER: "" + SCREENSCRAPER_PASSWORD: "" + RETROACHIEVEMENTS_API_KEY: "" + STEAMGRIDDB_API_KEY: "" ``` ## MariaDB @@ -81,52 +82,68 @@ A single-replica MariaDB is fine for most RomM deployments. Use a StatefulSet so apiVersion: apps/v1 kind: StatefulSet metadata: - name: romm-db - namespace: romm + name: romm-db + namespace: romm spec: - serviceName: romm-db - replicas: 1 - selector: - matchLabels: { app: romm-db } - template: - metadata: - labels: { app: romm-db } - spec: - enableServiceLinks: false - containers: - - name: mariadb - image: mariadb:11 - env: - - { name: MARIADB_DATABASE, value: romm } - - { name: MARIADB_USER, value: romm-user } - - name: MARIADB_PASSWORD - valueFrom: { secretKeyRef: { name: romm-secrets, key: DB_PASSWD } } - - name: MARIADB_ROOT_PASSWORD - valueFrom: { secretKeyRef: { name: romm-secrets, key: MARIADB_ROOT_PASSWORD } } - ports: - - { name: mysql, containerPort: 3306 } - volumeMounts: - - { name: data, mountPath: /var/lib/mysql } - readinessProbe: - exec: - command: ["healthcheck.sh", "--connect", "--innodb_initialized"] - periodSeconds: 10 - volumeClaimTemplates: - - metadata: { name: data } - spec: - accessModes: [ReadWriteOnce] - resources: { requests: { storage: 10Gi } } + serviceName: romm-db + replicas: 1 + selector: + matchLabels: { app: romm-db } + template: + metadata: + labels: { app: romm-db } + spec: + enableServiceLinks: false + containers: + - name: mariadb + image: mariadb:11 + env: + - { name: MARIADB_DATABASE, value: romm } + - { name: MARIADB_USER, value: romm-user } + - name: MARIADB_PASSWORD + valueFrom: + { + secretKeyRef: + { name: romm-secrets, key: DB_PASSWD }, + } + - name: MARIADB_ROOT_PASSWORD + valueFrom: + { + secretKeyRef: + { + name: romm-secrets, + key: MARIADB_ROOT_PASSWORD, + }, + } + ports: + - { name: mysql, containerPort: 3306 } + volumeMounts: + - { name: data, mountPath: /var/lib/mysql } + readinessProbe: + exec: + command: + [ + "healthcheck.sh", + "--connect", + "--innodb_initialized", + ] + periodSeconds: 10 + volumeClaimTemplates: + - metadata: { name: data } + spec: + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 10Gi } } --- apiVersion: v1 kind: Service metadata: - name: romm-db - namespace: romm + name: romm-db + namespace: romm spec: - clusterIP: None - ports: - - { name: mysql, port: 3306, targetPort: 3306 } - selector: { app: romm-db } + clusterIP: None + ports: + - { name: mysql, port: 3306, targetPort: 3306 } + selector: { app: romm-db } ``` ## RomM @@ -135,66 +152,84 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: - name: romm - namespace: romm + name: romm + namespace: romm spec: - replicas: 1 - selector: - matchLabels: { app: romm } - template: - metadata: - labels: { app: romm } - spec: - enableServiceLinks: false # ← required, see top of page - containers: - - name: romm - image: rommapp/romm:5.0.0 - env: - - { name: DB_HOST, value: romm-db } - - { name: DB_NAME, value: romm } - - { name: DB_USER, value: romm-user } - - name: DB_PASSWD - valueFrom: { secretKeyRef: { name: romm-secrets, key: DB_PASSWD } } - - name: ROMM_AUTH_SECRET_KEY - valueFrom: { secretKeyRef: { name: romm-secrets, key: ROMM_AUTH_SECRET_KEY } } - - { name: ROMM_BASE_URL, value: "https://romm.example.com" } - - { name: HASHEOUS_API_ENABLED, value: "true" } - # ... other metadata provider vars from the secret - envFrom: - - secretRef: { name: romm-secrets } - ports: - - { name: http, containerPort: 8080 } - volumeMounts: - - { name: library, mountPath: /romm/library, readOnly: true } - - { name: assets, mountPath: /romm/assets } - - { name: resources, mountPath: /romm/resources } - - { name: config, mountPath: /romm/config } - - { name: redis-data, mountPath: /redis-data } - readinessProbe: - httpGet: { path: /api/heartbeat, port: http } - initialDelaySeconds: 30 - periodSeconds: 10 - volumes: - - name: library - persistentVolumeClaim: { claimName: romm-library } - - name: assets - persistentVolumeClaim: { claimName: romm-assets } - - name: resources - persistentVolumeClaim: { claimName: romm-resources } - - name: config - persistentVolumeClaim: { claimName: romm-config } - - name: redis-data - persistentVolumeClaim: { claimName: romm-redis-data } + replicas: 1 + selector: + matchLabels: { app: romm } + template: + metadata: + labels: { app: romm } + spec: + enableServiceLinks: false # ← required, see top of page + containers: + - name: romm + image: rommapp/romm:5.0.0 + env: + - { name: DB_HOST, value: romm-db } + - { name: DB_NAME, value: romm } + - { name: DB_USER, value: romm-user } + - name: DB_PASSWD + valueFrom: + { + secretKeyRef: + { name: romm-secrets, key: DB_PASSWD }, + } + - name: ROMM_AUTH_SECRET_KEY + valueFrom: + { + secretKeyRef: + { + name: romm-secrets, + key: ROMM_AUTH_SECRET_KEY, + }, + } + - { + name: ROMM_BASE_URL, + value: "https://romm.example.com", + } + - { name: HASHEOUS_API_ENABLED, value: "true" } + # ... other metadata provider vars from the secret + envFrom: + - secretRef: { name: romm-secrets } + ports: + - { name: http, containerPort: 8080 } + volumeMounts: + - { + name: library, + mountPath: /romm/library, + readOnly: true, + } + - { name: assets, mountPath: /romm/assets } + - { name: resources, mountPath: /romm/resources } + - { name: config, mountPath: /romm/config } + - { name: redis-data, mountPath: /redis-data } + readinessProbe: + httpGet: { path: /api/heartbeat, port: http } + initialDelaySeconds: 30 + periodSeconds: 10 + volumes: + - name: library + persistentVolumeClaim: { claimName: romm-library } + - name: assets + persistentVolumeClaim: { claimName: romm-assets } + - name: resources + persistentVolumeClaim: { claimName: romm-resources } + - name: config + persistentVolumeClaim: { claimName: romm-config } + - name: redis-data + persistentVolumeClaim: { claimName: romm-redis-data } --- apiVersion: v1 kind: Service metadata: - name: romm - namespace: romm + name: romm + namespace: romm spec: - ports: - - { name: http, port: 80, targetPort: 8080 } - selector: { app: romm } + ports: + - { name: http, port: 80, targetPort: 8080 } + selector: { app: romm } ``` ## Persistent Volume Claims @@ -206,36 +241,36 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: { name: romm-library, namespace: romm } spec: - accessModes: [ReadOnlyMany] # your ROM store, typically an RWX mount - resources: { requests: { storage: 500Gi } } + accessModes: [ReadOnlyMany] # your ROM store, typically an RWX mount + resources: { requests: { storage: 500Gi } } --- apiVersion: v1 kind: PersistentVolumeClaim metadata: { name: romm-assets, namespace: romm } spec: - accessModes: [ReadWriteOnce] - resources: { requests: { storage: 20Gi } } + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 20Gi } } --- apiVersion: v1 kind: PersistentVolumeClaim metadata: { name: romm-resources, namespace: romm } spec: - accessModes: [ReadWriteOnce] - resources: { requests: { storage: 50Gi } } + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 50Gi } } --- apiVersion: v1 kind: PersistentVolumeClaim metadata: { name: romm-config, namespace: romm } spec: - accessModes: [ReadWriteOnce] - resources: { requests: { storage: 1Gi } } + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 1Gi } } --- apiVersion: v1 kind: PersistentVolumeClaim metadata: { name: romm-redis-data, namespace: romm } spec: - accessModes: [ReadWriteOnce] - resources: { requests: { storage: 5Gi } } + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 5Gi } } ``` For shared library access (multiple RomM replicas, or CI jobs that bulk-import), use RWX on the library PVC (NFS, CephFS, Longhorn-RWX). Assets must also be RWX if you ever run more than one RomM replica. @@ -246,29 +281,29 @@ For shared library access (multiple RomM replicas, or CI jobs that bulk-import), apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: romm - namespace: romm - annotations: - cert-manager.io/cluster-issuer: letsencrypt-prod - # Crucial: allow large uploads + websockets - nginx.ingress.kubernetes.io/proxy-body-size: "0" - nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" - nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + name: romm + namespace: romm + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + # Crucial: allow large uploads + websockets + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" spec: - ingressClassName: nginx - tls: - - hosts: [romm.example.com] - secretName: romm-tls - rules: - - host: romm.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: romm - port: { number: 80 } + ingressClassName: nginx + tls: + - hosts: [romm.example.com] + secretName: romm-tls + rules: + - host: romm.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: romm + port: { number: 80 } ``` `proxy-body-size: "0"` is important, because the default is 1 MB and will reject every ROM upload with HTTP 413. diff --git a/docs/install/redis-or-valkey.md b/docs/install/redis-or-valkey.md index e3aa6b78..3629c01e 100644 --- a/docs/install/redis-or-valkey.md +++ b/docs/install/redis-or-valkey.md @@ -30,36 +30,50 @@ Better for: ```yaml services: - romm: - environment: - - REDIS_HOST=romm-redis - - REDIS_PORT=6379 - - REDIS_PASSWD= - depends_on: - romm-redis: - condition: service_healthy - - romm-redis: - image: redis:7-alpine - command: ["redis-server", "--requirepass", "", "--appendonly", "yes"] - volumes: - - redis_data:/data - healthcheck: - test: ["CMD", "redis-cli", "-a", "", "ping"] - interval: 10s - timeout: 3s - retries: 5 + romm: + environment: + - REDIS_HOST=romm-redis + - REDIS_PORT=6379 + - REDIS_PASSWD= + depends_on: + romm-redis: + condition: service_healthy + + romm-redis: + image: redis:7-alpine + command: + [ + "redis-server", + "--requirepass", + "", + "--appendonly", + "yes", + ] + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "", "ping"] + interval: 10s + timeout: 3s + retries: 5 volumes: - redis_data: + redis_data: ``` ### Valkey drop-in ```yaml - romm-redis: +romm-redis: image: valkey/valkey:7-alpine - command: ["valkey-server", "--requirepass", "", "--appendonly", "yes"] + command: + [ + "valkey-server", + "--requirepass", + "", + "--appendonly", + "yes", + ] # everything else the same ``` @@ -69,12 +83,12 @@ If you've got an AWS ElastiCache / Upstash / Redis Cloud instance, point RomM at ```yaml environment: - - REDIS_HOST=my-redis.cache.amazonaws.com - - REDIS_PORT=6379 - - REDIS_USERNAME=romm # omit if your Redis uses legacy auth - - REDIS_PASSWORD= - - REDIS_SSL=true # for managed Redis, almost always yes - - REDIS_DB=0 # separate RomM from other apps on the same instance + - REDIS_HOST=my-redis.cache.amazonaws.com + - REDIS_PORT=6379 + - REDIS_USERNAME=romm # omit if your Redis uses legacy auth + - REDIS_PASSWORD= + - REDIS_SSL=true # for managed Redis, almost always yes + - REDIS_DB=0 # separate RomM from other apps on the same instance ``` The full list of Redis env vars lives in [Environment Variables](../reference/environment-variables.md). diff --git a/docs/install/reverse-proxy.md b/docs/install/reverse-proxy.md index 02f4502a..a7361a13 100644 --- a/docs/install/reverse-proxy.md +++ b/docs/install/reverse-proxy.md @@ -7,6 +7,7 @@ description: Put RomM behind Caddy, nginx, Traefik, or Nginx Proxy Manager with The RomM container listens on plain HTTP on port `8080`. For anything beyond `localhost` you should put it behind a reverse proxy that terminates TLS and forwards to the container. + !!! tip "WebSockets are required" RomM uses Socket.IO (both the general `/ws/socket.io` endpoint and the `/netplay/socket.io` endpoint) for live updates, scan progress, and Netplay. Every reverse-proxy recipe below keeps WebSocket support on, so don't strip it out. @@ -102,6 +103,7 @@ server { } ``` + !!! note "`client_max_body_size 0`" Required so large ROM uploads aren't rejected by nginx before they reach RomM. @@ -111,24 +113,24 @@ server { ```yaml http: - routers: - romm: - entryPoints: - - websecure - rule: "Host(`romm.mysite.com`)" - middlewares: - - default-headers - - https-redirectscheme - tls: - certResolver: letsencrypt - service: romm - - services: - romm: - loadBalancer: - servers: - - url: "http://192.168.1.100:8080" - passHostHeader: true + routers: + romm: + entryPoints: + - websecure + rule: "Host(`romm.mysite.com`)" + middlewares: + - default-headers + - https-redirectscheme + tls: + certResolver: letsencrypt + service: romm + + services: + romm: + loadBalancer: + servers: + - url: "http://192.168.1.100:8080" + passHostHeader: true ``` ### Docker Compose labels @@ -177,8 +179,8 @@ proxy_max_temp_file_size 0; Without that line, large downloads (bulk ROM zips, multi-disc games) will fail on NPM because nginx tries to buffer them to disk. -| Details | SSL | Advanced | -| --- | --- | --- | +| Details | SSL | Advanced | +| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | ![image](https://github.com/user-attachments/assets/e106a8e9-8b27-41ef-8ba2-d43c3b68b269) | ![image2](https://github.com/user-attachments/assets/6c82c785-792a-410a-80f2-d95839cba47b) | ![image3](https://github.com/user-attachments/assets/566ae834-99b5-42f3-b46b-306b8f73b5b4) | ## Set `ROMM_BASE_URL` behind HTTPS @@ -187,7 +189,7 @@ Once you're proxying through HTTPS, set `ROMM_BASE_URL` in the RomM container's ```yaml environment: - - ROMM_BASE_URL=https://romm.mysite.com + - ROMM_BASE_URL=https://romm.mysite.com ``` If you're also using OIDC, update `OIDC_REDIRECT_URI` to match. See [OIDC Setup](../administration/oidc/index.md). diff --git a/docs/install/synology.md b/docs/install/synology.md index 687b203b..9a931029 100644 --- a/docs/install/synology.md +++ b/docs/install/synology.md @@ -68,15 +68,16 @@ Recommended before the first scan. Full walkthrough in [Metadata Providers](../a ## 5. Docker Compose + !!! info "MariaDB 10.7 note" This guide pins MariaDB to **10.7** for stability on older DSM versions. MariaDB 11 works on DSM 7.2+, so bump the image tag if you like. The Synology-flavoured compose file: MariaDB on port `3309` externally (to avoid colliding with Synology's built-in MariaDB), UID/GID customisation, simplified healthcheck: ???+ example "docker-compose.yml" - ``` yaml +`yaml --8<-- "synology.docker-compose.yml" - ``` + ` Replace placeholder UIDs, GIDs, passwords, API keys, and `ROMM_AUTH_SECRET_KEY` with your own before starting. diff --git a/docs/install/truenas.md b/docs/install/truenas.md index 6c205a29..d9567f80 100644 --- a/docs/install/truenas.md +++ b/docs/install/truenas.md @@ -51,9 +51,9 @@ Use this path when the App Catalog has a bug, or when you want more flexibility Fill in the empty values with credentials you created in [Quick Start](../getting-started/quick-start.md). ???+ example "docker-compose.yml" - ``` yaml +`yaml --8<-- "truenas.docker-compose.yml" - ``` + ` ### 3. Install @@ -69,7 +69,7 @@ Save. Same troubleshooting applies, see below. ### Permission errors inside the container -If you're seeing permission errors on paths *inside* the RomM container (not on TrueNAS datasets), try temporarily running the container as root (`user: 0`) to unblock yourself, fix the offending permissions via shell, and switch back to a non-root user. +If you're seeing permission errors on paths _inside_ the RomM container (not on TrueNAS datasets), try temporarily running the container as root (`user: 0`) to unblock yourself, fix the offending permissions via shell, and switch back to a non-root user. In at least one reported setup, creating a user/group in TrueNAS with UID/GID `1000:1000` and the auxiliary `apps` group was needed to get RomM talking to its embedded Valkey cleanly. diff --git a/docs/install/unraid.md b/docs/install/unraid.md index 4a1a63a2..f5acb719 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -12,6 +12,7 @@ Two supported install paths on Unraid. Pick one: Both end up with the same running stack. + !!! warning "Back up `appdata` before updates" Tearing down the RomM container wipes its resources directory (covers, screenshots, cached metadata). Mount `appdata` on a safe path or [back it up](backup-and-restore.md) before every upgrade. @@ -41,6 +42,7 @@ Fill in the env vars. Names and sensible defaults live in the [reference `docker ![MariaDB environment variables](https://github.com/user-attachments/assets/a11906c5-25b2-46f1-906b-451a9ee39dca) + !!! warning "Network type" MariaDB's network type **must** be set to `Custom: romm`. Otherwise RomM can't resolve its hostname. @@ -91,6 +93,7 @@ Paste the [reference `docker-compose.yml`](docker-compose.md) and fill in your e Save after each edit. + !!! warning "Folder structure" Make sure your library matches one of the [supported folder layouts](../getting-started/folder-structure.md) before scanning. Unraid users often forget this step. diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index 74172f58..d6f03dfd 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -17,11 +17,11 @@ Make a folder for the platform under your library root. Rules: Examples: -| Folder name | Displays as | -| --- | --- | +| Folder name | Displays as | +| --------------------- | ------------------- | | `pocket-challenge-v2` | Pocket Challenge V2 | | `my-homebrew-console` | My Homebrew Console | -| `wasm4` | Wasm4 | +| `wasm4` | Wasm4 | Then either run a **Quick Scan** (the platform is auto-discovered) or trigger a **New Platforms** scan from the **Scan** page. @@ -31,10 +31,10 @@ If your platform is one RomM supports but under a different slug, you don't need ```yaml system: - platforms: - super_nintendo: "snes" # your folder → canonical slug - psx: "ps" - game-cube: "ngc" + platforms: + super_nintendo: "snes" # your folder → canonical slug + psx: "ps" + game-cube: "ngc" ``` This gets you full metadata-provider support with your preferred folder name. See [Configuration File → `system.platforms`](../reference/configuration-file.md#systemplatforms). @@ -51,9 +51,9 @@ Bind-mount a host directory onto the container's icon path: ```yaml services: - romm: - volumes: - - /path/to/your/icons:/var/www/html/assets/platforms + romm: + volumes: + - /path/to/your/icons:/var/www/html/assets/platforms ``` ### 2. Seed it with the official icons @@ -64,11 +64,11 @@ RomM's built-in icons live at [`frontend/assets/platforms`](https://github.com/r The filename has to **match the IGDB platform slug**. Examples: -| Platform | IGDB slug | Filename | -| --- | --- | --- | -| Amstrad CPC | `acpc` | `acpc.ico` | +| Platform | IGDB slug | Filename | +| ------------------- | ------------------------------ | ------------------------- | +| Amstrad CPC | `acpc` | `acpc.ico` | | Pocket Challenge V2 | `pocket-challenge-v2` (custom) | `pocket-challenge-v2.ico` | -| NES | `nes` | `nes.ico` | +| NES | `nes` | `nes.ico` | Find the slug in the URL of the platform's IGDB page, e.g. [igdb.com/platforms/acpc](https://www.igdb.com/platforms/acpc) → slug is `acpc`. diff --git a/docs/platforms/emulatorjs-config.md b/docs/platforms/emulatorjs-config.md index cb80d668..0cca0d7b 100644 --- a/docs/platforms/emulatorjs-config.md +++ b/docs/platforms/emulatorjs-config.md @@ -13,7 +13,7 @@ All settings below live under the `emulatorjs:` key in [`config.yml`](../referen ```yaml environment: - - ENABLE_EMULATORJS=true # default + - ENABLE_EMULATORJS=true # default ``` Set to `false` to disable EmulatorJS entirely: useful on the slim image or when running headless with companion apps only. The Play button is hidden, but Ruffle still works independently. See [Image Variants](../install/image-variants.md). @@ -22,7 +22,7 @@ Set to `false` to disable EmulatorJS entirely: useful on the slim image or when ```yaml emulatorjs: - debug: true + debug: true ``` Logs every available option for each core to the browser console when a game launches. Turn on when tuning, and turn off in production (it's verbose). @@ -31,7 +31,7 @@ Logs every available option for each core to the browser console when a game lau ```yaml emulatorjs: - cache_limit: 52428800 # 50 MB per ROM; null = unlimited + cache_limit: 52428800 # 50 MB per ROM; null = unlimited ``` Each ROM's cache (core files, BIOS, settings) is capped. `null` removes the cap: useful for large PSP / Saturn libraries on hosts with plenty of disk. @@ -40,8 +40,8 @@ Each ROM's cache (core files, BIOS, settings) is capped. `null` removes the cap: ```yaml emulatorjs: - disable_batch_bootup: true - disable_auto_unload: true + disable_batch_bootup: true + disable_auto_unload: true ``` - **`disable_batch_bootup`**: skips the `autorun.bat` step when `dosbox-pure` loads. Try this if DOS games hang on boot. See [MS-DOS](ms-dos.md). @@ -51,17 +51,18 @@ emulatorjs: ```yaml emulatorjs: - netplay: - enabled: true - ice_servers: - - urls: "stun:stun.l.google.com:19302" - - urls: "turn:openrelay.metered.ca:443" - username: "openrelayproject" - credential: "openrelayproject" + netplay: + enabled: true + ice_servers: + - urls: "stun:stun.l.google.com:19302" + - urls: "turn:openrelay.metered.ca:443" + username: "openrelayproject" + credential: "openrelayproject" ``` Full Netplay config in [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50) and user-side docs in [Netplay](../using/netplay.md). + !!! warning "Nightly CDN caveat" Enabling Netplay switches some assets to EmulatorJS's nightly CDN. Occasional hiccups are possible. If you don't need Netplay, leave it off. @@ -71,24 +72,24 @@ Every core exposes different options. Set defaults your users get pre-filled: ```yaml emulatorjs: - settings: - # Apply to all cores - default: - fps: show - - # Per-core - snes9x: - snes9x_region: ntsc - snes9x_layer_1: true - snes9x_overclock: false - - parallel_n64: - vsync: disable - parallel-n64-gfxplugin: "auto" - - ppsspp: - ppsspp_internal_resolution: "2" # 2x - ppsspp_frame_skipping: "0" + settings: + # Apply to all cores + default: + fps: show + + # Per-core + snes9x: + snes9x_region: ntsc + snes9x_layer_1: true + snes9x_overclock: false + + parallel_n64: + vsync: disable + parallel-n64-gfxplugin: "auto" + + ppsspp: + ppsspp_internal_resolution: "2" # 2x + ppsspp_frame_skipping: "0" ``` Finding core names and their options: @@ -104,19 +105,19 @@ Upstream reference: [EmulatorJS core options](https://emulatorjs.org/docs4devs/s ```yaml emulatorjs: - controls: - snes9x: - 0: # player 1 - 0: # B button slot (index 0) - value: x # keyboard key "x" - value2: BUTTON_2 # controller button 2 - 1: # Y button slot - value: z - value2: BUTTON_3 - 1: # player 2 - 0: - value: / - value2: BUTTON_2 + controls: + snes9x: + 0: # player 1 + 0: # B button slot (index 0) + value: x # keyboard key "x" + value2: BUTTON_2 # controller button 2 + 1: # Y button slot + value: z + value2: BUTTON_3 + 1: # player 2 + 0: + value: / + value2: BUTTON_2 ``` The structure: @@ -135,28 +136,28 @@ Users can override operator-level defaults in-game via Menu → **Controls**. Op ```yaml emulatorjs: - settings: - snes9x: - snes9x_region: ntsc - controls: - snes9x: - 0: # P1 on keyboard (WASD cluster) - 0: { value: ",", value2: "BUTTON_2" } # B - 1: { value: ".", value2: "BUTTON_3" } # A - 2: { value: "l", value2: "BUTTON_1" } # Y - 3: { value: "p", value2: "BUTTON_4" } # X - 1: # P2 on arrows + numpad - 0: { value: "/", value2: "BUTTON_2" } - 1: { value: "'", value2: "BUTTON_3" } + settings: + snes9x: + snes9x_region: ntsc + controls: + snes9x: + 0: # P1 on keyboard (WASD cluster) + 0: { value: ",", value2: "BUTTON_2" } # B + 1: { value: ".", value2: "BUTTON_3" } # A + 2: { value: "l", value2: "BUTTON_1" } # Y + 3: { value: "p", value2: "BUTTON_4" } # X + 1: # P2 on arrows + numpad + 0: { value: "/", value2: "BUTTON_2" } + 1: { value: "'", value2: "BUTTON_3" } ``` ## Per-user vs operator-level -| Where the setting is stored | Who it affects | Survives upgrades? | -| --- | --- | --- | -| Operator: `config.yml` / env vars | Everyone, as default | Yes | -| Per-user: in-game Menu → Settings | Just that user | Yes | -| Per-user: in-game Menu → Controls | Just that user | Yes | +| Where the setting is stored | Who it affects | Survives upgrades? | +| --------------------------------- | -------------------- | ------------------ | +| Operator: `config.yml` / env vars | Everyone, as default | Yes | +| Per-user: in-game Menu → Settings | Just that user | Yes | +| Per-user: in-game Menu → Controls | Just that user | Yes | Per-user overrides take precedence. A config.yml setting is the fallback. diff --git a/docs/platforms/firmware-by-platform.md b/docs/platforms/firmware-by-platform.md index 190ae4d3..1f7fa7d8 100644 --- a/docs/platforms/firmware-by-platform.md +++ b/docs/platforms/firmware-by-platform.md @@ -7,28 +7,29 @@ description: Per-platform firmware/BIOS requirements for in-browser play. This page lists the firmware RomM's EmulatorJS cores need for each platform. For the admin-side workflow (uploading, managing, bundling multi-file firmware), see [Firmware Management](../administration/firmware-management.md). + !!! note "Legality is your problem" RomM does not ship firmware. Legality of obtaining firmware varies by jurisdiction and by whether you own the hardware. The project can't help you acquire BIOS files. ## Platforms that need firmware -| Platform | Folder slug | Required files | Notes | -| --- | --- | --- | --- | -| **PlayStation (PSX)** | `ps` | `scph1001.bin` (US), `scph5501.bin` (US), `scph5502.bin` (EU), `scph7502.bin` (JP) | At least one region BIOS required. | -| **Saturn** | `saturn` | `saturn_bios.bin`, optional `mpr-17933.bin` (EU) | Bundle both in a zip. | -| **Sega CD / Mega CD** | `segacd` | `bios_CD_U.bin` (US), `bios_CD_E.bin` (EU), `bios_CD_J.bin` (JP) | Region depends on your games. | -| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional. Games run without it, but accurate emulation needs it. | -| **Nintendo DS** | `nds` | `bios7.bin`, `bios9.bin`, `firmware.bin` | All three are needed. | -| **Atari Lynx** | `lynx` | `lynxboot.img` | | -| **Neo Geo Pocket / Color** | `ngp` / `ngpc` | `neopop.rom` | Bundle as zip. | -| **Amiga** | `amiga` | Kickstart ROMs (various) | `puae` core. Kickstart 1.2, 1.3, 3.1 depending on title. Bundle as zip. | -| **ColecoVision** | `colecovision` | `coleco.rom` | | -| **Intellivision** | `intellivision` | `exec.bin`, `grom.bin` | Bundle both. | -| **3DO** | `3do` | `panafz10.bin` (or similar region BIOS) | | -| **PC Engine CD / TurboGrafx-CD** | `pcecd` / `tg16cd` | `syscard3.pce` | | -| **Dreamcast** | `dc` | `dc_boot.bin`, `dc_flash.bin` | EmulatorJS Dreamcast support is limited, so results vary. | -| **Atari 5200** | `atari5200` | `5200.rom` | | -| **Atari 7800** | `atari7800` | `7800 BIOS (U).rom` | | +| Platform | Folder slug | Required files | Notes | +| -------------------------------- | ------------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| **PlayStation (PSX)** | `ps` | `scph1001.bin` (US), `scph5501.bin` (US), `scph5502.bin` (EU), `scph7502.bin` (JP) | At least one region BIOS required. | +| **Saturn** | `saturn` | `saturn_bios.bin`, optional `mpr-17933.bin` (EU) | Bundle both in a zip. | +| **Sega CD / Mega CD** | `segacd` | `bios_CD_U.bin` (US), `bios_CD_E.bin` (EU), `bios_CD_J.bin` (JP) | Region depends on your games. | +| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional. Games run without it, but accurate emulation needs it. | +| **Nintendo DS** | `nds` | `bios7.bin`, `bios9.bin`, `firmware.bin` | All three are needed. | +| **Atari Lynx** | `lynx` | `lynxboot.img` | | +| **Neo Geo Pocket / Color** | `ngp` / `ngpc` | `neopop.rom` | Bundle as zip. | +| **Amiga** | `amiga` | Kickstart ROMs (various) | `puae` core. Kickstart 1.2, 1.3, 3.1 depending on title. Bundle as zip. | +| **ColecoVision** | `colecovision` | `coleco.rom` | | +| **Intellivision** | `intellivision` | `exec.bin`, `grom.bin` | Bundle both. | +| **3DO** | `3do` | `panafz10.bin` (or similar region BIOS) | | +| **PC Engine CD / TurboGrafx-CD** | `pcecd` / `tg16cd` | `syscard3.pce` | | +| **Dreamcast** | `dc` | `dc_boot.bin`, `dc_flash.bin` | EmulatorJS Dreamcast support is limited, so results vary. | +| **Atari 5200** | `atari5200` | `5200.rom` | | +| **Atari 7800** | `atari7800` | `7800 BIOS (U).rom` | | For cores + platforms not in this table (most Nintendo consoles such as NES, SNES, N64, plus Sega Genesis, Atari 2600, Game Boy, most home computers), **no firmware is needed**. diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md index daca9a05..2b00955f 100644 --- a/docs/platforms/ms-dos.md +++ b/docs/platforms/ms-dos.md @@ -9,9 +9,11 @@ description: How to run DOS games in RomM via dosbox-pure, homebrew, shareware d DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-pure), the EmulatorJS integration: create a DOS platform (folder named `dos`) and drop your games in. + !!! tip "Upload games as `.zip`" `dosbox-pure` knows how to unzip and auto-mount zipped DOS games, which is much easier than packaging a raw folder. + !!! info "Save states work" Once you've got a game running, save state and resume from there, so you only need to do the boot dance once per game. @@ -154,6 +156,7 @@ Retail games need the game CD image mounted alongside the install directory. ``` Key lines: + - `Mount C ".."`: game files as `C:`. - `imgmount d DUNGEO~8.CUE -t iso -fs iso`: CD image as `D:`. - `cd ..`: back to `C:\` where `KEEPER.exe` lives. @@ -163,6 +166,7 @@ Retail games need the game CD image mounted alongside the install directory. 4. Zip, upload, play. + !!! info "GOG DOS re-releases don't work" GOG's DOS games ship with custom wrappers (DOSBox Staging, ScummVM scripts, etc.) that don't translate to `dosbox-pure`: 100% failure rate observed. For GOG titles, extract the underlying game files and build a `.conf` yourself. diff --git a/docs/platforms/ruffle-config.md b/docs/platforms/ruffle-config.md index ad11dad9..e114a754 100644 --- a/docs/platforms/ruffle-config.md +++ b/docs/platforms/ruffle-config.md @@ -11,7 +11,7 @@ description: Operator-level setup for the Ruffle Flash / Shockwave player. ```yaml environment: - - ENABLE_RUFFLE=true # default + - ENABLE_RUFFLE=true # default ``` Set `false` to hide the Ruffle Play button globally. Useful on the slim image (Ruffle isn't bundled) or when you have no Flash content. @@ -30,8 +30,8 @@ Just rename the folder to `flash/` and rescan. ```yaml system: - platforms: - web-games: "flash" # your-folder: canonical-slug + platforms: + web-games: "flash" # your-folder: canonical-slug ``` Now `web-games/` is treated as the `flash` platform. See [Configuration File → `system.platforms`](../reference/configuration-file.md#systemplatforms). @@ -60,7 +60,7 @@ Metadata coverage for Flash games comes from the [Flashpoint](../administration/ ```yaml environment: - - FLASHPOINT_API_ENABLED=true + - FLASHPOINT_API_ENABLED=true ``` Then run an **Unmatched** scan on your `flash` platform. Titles, descriptions, cover art, tags: all populated if the game exists in Flashpoint's ~180,000-entry database. diff --git a/docs/reference/configuration-file.md b/docs/reference/configuration-file.md index ee85e92d..d7e15183 100644 --- a/docs/reference/configuration-file.md +++ b/docs/reference/configuration-file.md @@ -14,6 +14,7 @@ Start from the [`config.example.yml`](https://github.com/rommapp/romm/blob/maste - [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml) - [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml) + !!! warning "Only set what you need" Any omitted section uses the default. Don't copy the full example and then strip sections. Just add what you want to change. @@ -29,7 +30,7 @@ Skip entire platform folders. Values are platform slugs, not folder names. See [ ```yaml exclude: - platforms: ["ps", "ngc", "gba"] + platforms: ["ps", "ngc", "gba"] ``` ### `exclude.roms.single_file.extensions` @@ -40,9 +41,9 @@ Drop files with these extensions before matching. Applies to files that aren't i ```yaml exclude: - roms: - single_file: - extensions: ["xml", "txt"] + roms: + single_file: + extensions: ["xml", "txt"] ``` ### `exclude.roms.single_file.names` @@ -53,9 +54,9 @@ Unix-glob file-name patterns to skip. ```yaml exclude: - roms: - single_file: - names: ["info.txt", "._*", "*.nfo"] + roms: + single_file: + names: ["info.txt", "._*", "*.nfo"] ``` ### `exclude.roms.multi_file.names` @@ -66,9 +67,9 @@ Skip whole folders. Used for multi-disc / multi-file games you want invisible. ```yaml exclude: - roms: - multi_file: - names: ["final fantasy VII", "DLC"] + roms: + multi_file: + names: ["final fantasy VII", "DLC"] ``` ### `exclude.roms.multi_file.parts.names` @@ -79,10 +80,10 @@ Files **inside** a multi-file ROM folder to ignore. Useful for excluding `.nfo`, ```yaml exclude: - roms: - multi_file: - parts: - names: ["data.xml", "._*"] + roms: + multi_file: + parts: + names: ["data.xml", "._*"] ``` ### `exclude.roms.multi_file.parts.extensions` @@ -93,10 +94,10 @@ Extensions to ignore inside a multi-file ROM folder. ```yaml exclude: - roms: - multi_file: - parts: - extensions: ["xml", "txt"] + roms: + multi_file: + parts: + extensions: ["xml", "txt"] ``` --- @@ -111,10 +112,10 @@ Map your folder names to RomM platform slugs. Left side = your folder name, righ ```yaml system: - platforms: - gc: "ngc" # treat "gc/" folder as GameCube - psx: "ps" # treat "psx/" folder as PlayStation - super_nintendo: "snes" + platforms: + gc: "ngc" # treat "gc/" folder as GameCube + psx: "ps" # treat "psx/" folder as PlayStation + super_nintendo: "snes" ``` ### `system.versions` @@ -123,8 +124,8 @@ Associate a platform with its "main" IGDB version. Useful for platforms that hav ```yaml system: - versions: - naomi: "arcade" + versions: + naomi: "arcade" ``` --- @@ -137,7 +138,7 @@ Override the default ROMs folder name (`roms`). ```yaml filesystem: - roms_folder: "my_roms" + roms_folder: "my_roms" ``` ### `filesystem.firmware_folder` _(new in 5.0)_ @@ -146,7 +147,7 @@ Override the default BIOS/firmware folder name (`bios`). ```yaml filesystem: - firmware_folder: "firmware" + firmware_folder: "firmware" ``` ### `filesystem.skip_hash_calculation` @@ -157,7 +158,7 @@ Skip hashing on low-power devices. You lose hash-based matching (RetroAchievemen ```yaml filesystem: - skip_hash_calculation: true + skip_hash_calculation: true ``` --- @@ -172,28 +173,28 @@ Order metadata providers are queried during a scan. First match wins for descrip ```yaml scan: - priority: - metadata: - - "igdb" - - "ss" - - "moby" + priority: + metadata: + - "igdb" + - "ss" + - "moby" ``` Values are the provider slugs. Full list: -| Slug | Provider | -| --- | --- | -| `igdb` | IGDB | -| `moby` | MobyGames | -| `ss` | ScreenScraper | -| `ra` | RetroAchievements | -| `launchbox` | LaunchBox | -| `gamelist` | gamelist.xml importer | -| `hasheous` | Hasheous | -| `flashpoint` | Flashpoint | -| `hltb` | HowLongToBeat | -| `tgdb` | TheGamesDB _(5.0)_ | -| `libretro` | Libretro metadata _(5.0)_ | +| Slug | Provider | +| ------------ | ------------------------- | +| `igdb` | IGDB | +| `moby` | MobyGames | +| `ss` | ScreenScraper | +| `ra` | RetroAchievements | +| `launchbox` | LaunchBox | +| `gamelist` | gamelist.xml importer | +| `hasheous` | Hasheous | +| `flashpoint` | Flashpoint | +| `hltb` | HowLongToBeat | +| `tgdb` | TheGamesDB _(5.0)_ | +| `libretro` | Libretro metadata _(5.0)_ | See [Metadata Providers](../administration/metadata-providers.md) for context on each. @@ -203,11 +204,11 @@ Same idea, for cover art and screenshots. Defaults to the same order as `scan.pr ```yaml scan: - priority: - artwork: - - "ss" # prefer ScreenScraper artwork - - "igdb" - - "moby" + priority: + artwork: + - "ss" # prefer ScreenScraper artwork + - "igdb" + - "moby" ``` ### `scan.priority.region` _(enhanced in 5.0)_ @@ -218,11 +219,11 @@ Preferred region for titles, cover art, and regional variants. ScreenScraper use ```yaml scan: - priority: - region: - - "us" - - "eu" - - "jp" + priority: + region: + - "us" + - "eu" + - "jp" ``` ### `scan.priority.language` _(enhanced in 5.0)_ @@ -233,38 +234,38 @@ Preferred localisation language. ```yaml scan: - priority: - language: - - "en" - - "es" - - "fr" + priority: + language: + - "en" + - "es" + - "fr" ``` ### `scan.media` Which media types to fetch during a scan. Applies primarily to ScreenScraper and the gamelist.xml importer. -| Type | Description | -| --- | --- | -| `box2d` | Normal 2D cover art. Always enabled. | -| `box3d` | 3D box art. | -| `miximage` | Composite image (box + screenshot + logo). | -| `physical` | Physical media (disc, cartridge). | -| `screenshot` | In-game screenshot. Enabled by default. | -| `title_screen` | Title-screen capture. | -| `marquee` | Transparent logo. | -| `fanart` | Community-uploaded fan art. | -| `bezel` | EmulatorJS-compatible bezel. | -| `manual` | PDF manual. Enabled by default. | -| `video` | Gameplay video (big files, watch your disk). | +| Type | Description | +| -------------- | -------------------------------------------- | +| `box2d` | Normal 2D cover art. Always enabled. | +| `box3d` | 3D box art. | +| `miximage` | Composite image (box + screenshot + logo). | +| `physical` | Physical media (disc, cartridge). | +| `screenshot` | In-game screenshot. Enabled by default. | +| `title_screen` | Title-screen capture. | +| `marquee` | Transparent logo. | +| `fanart` | Community-uploaded fan art. | +| `bezel` | EmulatorJS-compatible bezel. | +| `manual` | PDF manual. Enabled by default. | +| `video` | Gameplay video (big files, watch your disk). | ```yaml scan: - media: - - box2d - - screenshot - - manual - - bezel + media: + - box2d + - screenshot + - manual + - bezel ``` ### `scan.gamelist.export` _(new in 5.0)_ @@ -273,11 +274,11 @@ Generate a `gamelist.xml` in each platform folder, compatible with ES-DE / Batoc ```yaml scan: - gamelist: - export: true - media: - thumbnail: box2d - image: screenshot + gamelist: + export: true + media: + thumbnail: box2d + image: screenshot ``` ### `scan.pegasus.export` _(new in 5.0)_ @@ -286,8 +287,8 @@ Export metadata in Pegasus-frontend format (`metadata.pegasus.txt`). ```yaml scan: - pegasus: - export: true + pegasus: + export: true ``` --- @@ -304,7 +305,7 @@ Log available EmulatorJS options to the browser console for debugging. ```yaml emulatorjs: - debug: true + debug: true ``` ### `emulatorjs.cache_limit` @@ -313,7 +314,7 @@ Per-ROM cache limit in bytes. `null` = unlimited. ```yaml emulatorjs: - cache_limit: 52428800 # 50 MB + cache_limit: 52428800 # 50 MB ``` ### `emulatorjs.disable_batch_bootup` @@ -322,7 +323,7 @@ DOS-specific. Skips the `autorun.bat` step. Try toggling if DOS games won't boot ```yaml emulatorjs: - disable_batch_bootup: true + disable_batch_bootup: true ``` ### `emulatorjs.disable_auto_unload` @@ -331,7 +332,7 @@ By default, EmulatorJS stops the emulator when you leave its page. Disable to ke ```yaml emulatorjs: - disable_auto_unload: true + disable_auto_unload: true ``` ### `emulatorjs.netplay` _(new in 5.0)_ @@ -340,20 +341,21 @@ Toggle Netplay and configure STUN/TURN servers. Google's public STUN servers are ```yaml emulatorjs: - netplay: - enabled: true - ice_servers: - - urls: "stun:stun.l.google.com:19302" - - urls: "stun:stun1.l.google.com:19302" - - urls: "stun:stun2.l.google.com:19302" - - urls: "turn:openrelay.metered.ca:80" - username: "openrelayproject" - credential: "openrelayproject" - - urls: "turn:openrelay.metered.ca:443" - username: "openrelayproject" - credential: "openrelayproject" + netplay: + enabled: true + ice_servers: + - urls: "stun:stun.l.google.com:19302" + - urls: "stun:stun1.l.google.com:19302" + - urls: "stun:stun2.l.google.com:19302" + - urls: "turn:openrelay.metered.ca:80" + username: "openrelayproject" + credential: "openrelayproject" + - urls: "turn:openrelay.metered.ca:443" + username: "openrelayproject" + credential: "openrelayproject" ``` + !!! note "Nightly CDN caveat" With Netplay enabled, EmulatorJS loads some assets (localisations included) from its nightly CDN (`https://cdn.emulatorjs.org/nightly/...`). Occasional 404s or untranslated strings can appear when the nightly has a transient mismatch. Usually self-heals by the next RomM image update. @@ -363,13 +365,13 @@ Per-core emulator options. Use `default` to apply to every core. ```yaml emulatorjs: - settings: - parallel_n64: - vsync: disable - snes9x: - snes9x_region: ntsc - default: - fps: show + settings: + parallel_n64: + vsync: disable + snes9x: + snes9x_region: ntsc + default: + fps: show ``` Core names must match the EmulatorJS core identifier exactly. See the `getSupportedEJSCores` utility in the frontend source for the full list, or leave the core out and use `default`. @@ -380,16 +382,16 @@ Map keyboard and controller buttons per core, per player. ```yaml emulatorjs: - controls: - snes9x: - 0: # player 1 - 0: # button slot - value: x # keyboard key - value2: BUTTON_2 # controller button - 1: # player 2 - 0: - value: / - value2: BUTTON_2 + controls: + snes9x: + 0: # player 1 + 0: # button slot + value: x # keyboard key + value2: BUTTON_2 # controller button + 1: # player 2 + 0: + value: / + value2: BUTTON_2 ``` See the [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/) for the button-slot reference. diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 297afbf1..fb6dd627 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -17,20 +17,20 @@ This page is the **authoritative lookup**: every var RomM reads. The table is ge ```yaml services: - romm: - environment: - - ROMM_AUTH_SECRET_KEY=abcd1234... - - DB_PASSWD=secure-password - # ... + romm: + environment: + - ROMM_AUTH_SECRET_KEY=abcd1234... + - DB_PASSWD=secure-password + # ... ``` Or from a `.env` file next to your compose: ```yaml services: - romm: - env_file: - - .env + romm: + env_file: + - .env ``` ### Secrets @@ -45,11 +45,11 @@ Don't embed `ROMM_AUTH_SECRET_KEY`, DB passwords, or provider API keys directly You'll always set these: -| Variable | Purpose | -| --- | --- | -| `ROMM_AUTH_SECRET_KEY` | JWT signing key generated with `openssl rand -hex 32`. **Never rotate lightly**, as it breaks all sessions. | -| `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | -| `ROMM_DB_DRIVER` | One of `mariadb` (default), `mysql`, `postgresql`, or `sqlite`: see [Databases](../install/databases.md). | +| Variable | Purpose | +| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `ROMM_AUTH_SECRET_KEY` | JWT signing key generated with `openssl rand -hex 32`. **Never rotate lightly**, as it breaks all sessions. | +| `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWD` | Database connection. | +| `ROMM_DB_DRIVER` | One of `mariadb` (default), `mysql`, `postgresql`, or `sqlite`: see [Databases](../install/databases.md). | For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../administration/metadata-providers.md). For OIDC, see [OIDC Setup](../administration/oidc/index.md). diff --git a/docs/reference/exports.md b/docs/reference/exports.md index 01c4c58d..07778dba 100644 --- a/docs/reference/exports.md +++ b/docs/reference/exports.md @@ -15,11 +15,11 @@ ES-DE, Batocera, and compatibles look for a `gamelist.xml` in each platform fold ```yaml scan: - gamelist: - export: true - media: - thumbnail: box2d # which media type to use as thumbnail - image: screenshot # which as full image + gamelist: + export: true + media: + thumbnail: box2d # which media type to use as thumbnail + image: screenshot # which as full image ``` With `export: true`, every scan writes a `gamelist.xml` into the platform folder, and downloads the selected media into sibling folders (`covers/`, `screenshots/`, etc.) that ES-DE expects. @@ -73,7 +73,7 @@ Once RomM has generated `gamelist.xml` and populated `covers/` + `screenshots/`, - `MediaDirectory`: point it at the ROM folder (same path ES-DE uses for `ROMDirectory`), so ES-DE looks for media in-place rather than in its own library. - `LegacyGamelistFileLocation`: makes ES-DE write updates back to the same `gamelist.xml` RomM reads from, rather than its separate config dir. -See also [Metadata Providers → gamelist.xml](../administration/metadata-providers.md) for the *import* direction (reading gamelist.xml into RomM). +See also [Metadata Providers → gamelist.xml](../administration/metadata-providers.md) for the _import_ direction (reading gamelist.xml into RomM). ## Pegasus @@ -83,8 +83,8 @@ See also [Metadata Providers → gamelist.xml](../administration/metadata-provid ```yaml scan: - pegasus: - export: true + pegasus: + export: true ``` ### Format diff --git a/docs/reference/feeds.md b/docs/reference/feeds.md index c0b5cd89..c72d9ef3 100644 --- a/docs/reference/feeds.md +++ b/docs/reference/feeds.md @@ -11,25 +11,25 @@ Feeds are **read-only**. They expose download URLs back to RomM's own `/api/...` ## Endpoint catalogue -| Feed | URL | Purpose | -| --- | --- | --- | -| **Tinfoil** | `/api/feeds/tinfoil` | Nintendo Switch `.nsp` / `.xci` installer. [Setup →](../ecosystem/tinfoil.md) | -| **pkgi (PS Vita games)** | `/api/feeds/pkgi/psvita/game` | PS Vita `.pkg` installer. [Setup →](../ecosystem/pkgj.md) | -| **pkgi (PS Vita DLCs)** | `/api/feeds/pkgi/psvita/dlc` | Same, DLC content. | -| **pkgi (PSP games)** | `/api/feeds/pkgi/psp/game` | PSP `.pkg` installer. | -| **pkgi (PSP DLCs)** | `/api/feeds/pkgi/psp/dlc` | Same, DLC content. | -| **fpkgi (PS4)** | `/api/feeds/fpkgi/ps4` | PS4 `.pkg` installer. [Setup →](../ecosystem/fpkgi.md) | -| **fpkgi (PS5)** | `/api/feeds/fpkgi/ps5` | PS5 `.pkg` installer. | -| **Kekatsu (NDS)** | `/api/feeds/kekatsu/nds` | Nintendo DS multiboot loader. [Setup →](../ecosystem/kekatsu.md) | -| **WebRcade** | `/api/feeds/webrcade` | Browser-based retro frontend. [Setup →](../ecosystem/webrcade.md) | +| Feed | URL | Purpose | +| ------------------------ | ----------------------------- | ----------------------------------------------------------------------------- | +| **Tinfoil** | `/api/feeds/tinfoil` | Nintendo Switch `.nsp` / `.xci` installer. [Setup →](../ecosystem/tinfoil.md) | +| **pkgi (PS Vita games)** | `/api/feeds/pkgi/psvita/game` | PS Vita `.pkg` installer. [Setup →](../ecosystem/pkgj.md) | +| **pkgi (PS Vita DLCs)** | `/api/feeds/pkgi/psvita/dlc` | Same, DLC content. | +| **pkgi (PSP games)** | `/api/feeds/pkgi/psp/game` | PSP `.pkg` installer. | +| **pkgi (PSP DLCs)** | `/api/feeds/pkgi/psp/dlc` | Same, DLC content. | +| **fpkgi (PS4)** | `/api/feeds/fpkgi/ps4` | PS4 `.pkg` installer. [Setup →](../ecosystem/fpkgi.md) | +| **fpkgi (PS5)** | `/api/feeds/fpkgi/ps5` | PS5 `.pkg` installer. | +| **Kekatsu (NDS)** | `/api/feeds/kekatsu/nds` | Nintendo DS multiboot loader. [Setup →](../ecosystem/kekatsu.md) | +| **WebRcade** | `/api/feeds/webrcade` | Browser-based retro frontend. [Setup →](../ecosystem/webrcade.md) | Plus legacy `pkgj` formats for individual platforms: -| Feed | URL | -| --- | --- | -| PKGj PSX | `/api/feeds/pkgj/psx` | +| Feed | URL | +| ------------ | ------------------------ | +| PKGj PSX | `/api/feeds/pkgj/psx` | | PKGj PS Vita | `/api/feeds/pkgj/psvita` | -| PKGj PSP | `/api/feeds/pkgj/psp` | +| PKGj PSP | `/api/feeds/pkgj/psp` | ## Authentication diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index ea69120d..0dcbc6ac 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -9,21 +9,21 @@ Quick reference for the ports + URL paths a RomM instance exposes. ## Container ports -| Port | Protocol | Purpose | Exposed? | -| --- | --- | --- | --- | -| `8080` | HTTP | nginx: the front door. Serves everything. | Yes, publish this. | -| `5000` | HTTP | gunicorn: FastAPI backend. Internal only. | No. nginx proxies here. | -| `6379` | TCP | Valkey. Internal only (unless you externalise). | No. | +| Port | Protocol | Purpose | Exposed? | +| ------ | -------- | ----------------------------------------------- | ----------------------- | +| `8080` | HTTP | nginx: the front door. Serves everything. | Yes, publish this. | +| `5000` | HTTP | gunicorn: FastAPI backend. Internal only. | No. nginx proxies here. | +| `6379` | TCP | Valkey. Internal only (unless you externalise). | No. | Only **`8080`** should be reachable from outside the container in production. Typical compose `ports:`: ```yaml services: - romm: - ports: - - 80:8080 # bind host :80 to container :8080 - # or behind a reverse proxy: - # - 127.0.0.1:8080:8080 + romm: + ports: + - 80:8080 # bind host :80 to container :8080 + # or behind a reverse proxy: + # - 127.0.0.1:8080:8080 ``` ## URL paths on `:8080` @@ -32,15 +32,15 @@ Everything below is served by nginx on port 8080. Auth / protection depends on t ### Unauthenticated -| Path | Purpose | -| --- | --- | -| `/login`, `/register`, `/reset-password` | Auth UI pages. | -| `/api/auth/login`, `/api/auth/logout` | Session endpoints. | -| `/api/auth/openid`, `/api/auth/oauth/openid` | OIDC callback flow. | -| `/api/heartbeat` | Health check. | -| `/openapi.json` | OpenAPI spec. | -| `/api/docs`, `/api/redoc` | Rendered API browsers. | -| Static assets (`/assets/...`, EmulatorJS bundle, Ruffle, covers served from `/resources/...`) | Served by nginx. | +| Path | Purpose | +| --------------------------------------------------------------------------------------------- | ---------------------- | +| `/login`, `/register`, `/reset-password` | Auth UI pages. | +| `/api/auth/login`, `/api/auth/logout` | Session endpoints. | +| `/api/auth/openid`, `/api/auth/oauth/openid` | OIDC callback flow. | +| `/api/heartbeat` | Health check. | +| `/openapi.json` | OpenAPI spec. | +| `/api/docs`, `/api/redoc` | Rendered API browsers. | +| Static assets (`/assets/...`, EmulatorJS bundle, Ruffle, covers served from `/resources/...`) | Served by nginx. | ### Session-authenticated (cookies) @@ -52,31 +52,31 @@ Every API endpoint under `/api/...` that requires a specific scope. Session cook ### Download endpoints (optionally auth-off) -| Path | Required scope normally | With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` | -| --- | --- | --- | -| `/api/roms/{id}/content/{filename}` | `roms.read` | None | -| `/api/firmware/{id}/content/{filename}` | `firmware.read` | None | +| Path | Required scope normally | With `DISABLE_DOWNLOAD_ENDPOINT_AUTH=true` | +| --------------------------------------- | ----------------------- | ------------------------------------------ | +| `/api/roms/{id}/content/{filename}` | `roms.read` | None | +| `/api/firmware/{id}/content/{filename}` | `firmware.read` | None | See [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass) for the security context. ### Feed endpoints -| Path | Client | Auth | -| --- | --- | --- | -| `/api/feeds/tinfoil` | [Tinfoil](../ecosystem/tinfoil.md) | Respects `DISABLE_DOWNLOAD_ENDPOINT_AUTH`, and can send basic auth. | -| `/api/feeds/pkgi/...` | [pkgj](../ecosystem/pkgj.md) | Same. | -| `/api/feeds/fpkgi/...` | [fpkgi](../ecosystem/fpkgi.md) | Same. | -| `/api/feeds/kekatsu/...` | [Kekatsu](../ecosystem/kekatsu.md) | Same. | -| `/api/feeds/webrcade` | [WebRcade](../ecosystem/webrcade.md) | Same. | +| Path | Client | Auth | +| ------------------------ | ------------------------------------ | ------------------------------------------------------------------- | +| `/api/feeds/tinfoil` | [Tinfoil](../ecosystem/tinfoil.md) | Respects `DISABLE_DOWNLOAD_ENDPOINT_AUTH`, and can send basic auth. | +| `/api/feeds/pkgi/...` | [pkgj](../ecosystem/pkgj.md) | Same. | +| `/api/feeds/fpkgi/...` | [fpkgi](../ecosystem/fpkgi.md) | Same. | +| `/api/feeds/kekatsu/...` | [Kekatsu](../ecosystem/kekatsu.md) | Same. | +| `/api/feeds/webrcade` | [WebRcade](../ecosystem/webrcade.md) | Same. | Full catalogue in [Feeds](feeds.md). ### WebSocket endpoints -| Path | Purpose | -| --- | --- | -| `/ws/socket.io` | General live updates (scans, tasks). | -| `/netplay/socket.io` | Netplay session coordination. | +| Path | Purpose | +| -------------------- | ------------------------------------ | +| `/ws/socket.io` | General live updates (scans, tasks). | +| `/netplay/socket.io` | Netplay session coordination. | Both use Socket.IO, so the reverse proxy must pass through the upgrade. See [WebSockets](../developers/websockets.md) and [Reverse Proxy](../install/reverse-proxy.md). @@ -84,13 +84,13 @@ Both use Socket.IO, so the reverse proxy must pass through the upgrade. See [Web Inside the container: -| Path | Purpose | Backup? | -| --- | --- | --- | -| `/romm/library` | Your ROM + firmware source. Typically read-only mount. | No (back up separately). | -| `/romm/assets` | User uploads (saves, states, screenshots). | **Critical.** | -| `/romm/resources` | Provider-fetched cover art, screenshots. | Low priority (rebuildable). | -| `/romm/config` | `config.yml`. | **Critical.** | -| `/redis-data` | Persistence for the in-container Valkey. | Low priority. | +| Path | Purpose | Backup? | +| ----------------- | ------------------------------------------------------ | --------------------------- | +| `/romm/library` | Your ROM + firmware source. Typically read-only mount. | No (back up separately). | +| `/romm/assets` | User uploads (saves, states, screenshots). | **Critical.** | +| `/romm/resources` | Provider-fetched cover art, screenshots. | Low priority (rebuildable). | +| `/romm/config` | `config.yml`. | **Critical.** | +| `/redis-data` | Persistence for the in-container Valkey. | Low priority. | See [Install & Deploy → Docker Compose](../install/docker-compose.md) for the full volume spec. @@ -98,18 +98,18 @@ See [Install & Deploy → Docker Compose](../install/docker-compose.md) for the Outbound connections a running RomM instance may make: -| Destination | Purpose | Optional? | -| --- | --- | --- | -| `api.igdb.com` | IGDB metadata. | Yes (if `IGDB_CLIENT_ID` set). | -| `www.screenscraper.fr` | ScreenScraper metadata. | Yes. | -| `www.mobygames.com` | MobyGames metadata. | Yes. | -| `retroachievements.org` | RA metadata + progression. | Yes. | -| `www.steamgriddb.com` | Cover art. | Yes. | -| `gamesdb.launchbox-app.com` | LaunchBox DB download. | Yes. | -| `hasheous.org` | Hash-based matching. | Yes. | -| Your OIDC provider | SSO. | Yes. | -| `sentry.io` or self-hosted Sentry | Error reporting. | Yes. | -| Your OTEL collector | Observability. | Yes. | +| Destination | Purpose | Optional? | +| --------------------------------- | -------------------------- | ------------------------------ | +| `api.igdb.com` | IGDB metadata. | Yes (if `IGDB_CLIENT_ID` set). | +| `www.screenscraper.fr` | ScreenScraper metadata. | Yes. | +| `www.mobygames.com` | MobyGames metadata. | Yes. | +| `retroachievements.org` | RA metadata + progression. | Yes. | +| `www.steamgriddb.com` | Cover art. | Yes. | +| `gamesdb.launchbox-app.com` | LaunchBox DB download. | Yes. | +| `hasheous.org` | Hash-based matching. | Yes. | +| Your OIDC provider | SSO. | Yes. | +| `sentry.io` or self-hosted Sentry | Error reporting. | Yes. | +| Your OTEL collector | Observability. | Yes. | If your firewall is egress-restrictive, allow-list these based on which features you've enabled. @@ -119,7 +119,7 @@ Some users put RomM behind a path prefix (`/romm/...` instead of the bare host r ```yaml environment: - - ROMM_BASE_PATH=/romm + - ROMM_BASE_PATH=/romm ``` nginx inside the container rewrites paths internally, and your reverse proxy forwards the full path. diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md index 3baa05e7..1ace5c2d 100644 --- a/docs/releases/changelog.md +++ b/docs/releases/changelog.md @@ -17,17 +17,14 @@ For migration-grade detail on a major version, go straight to that version's gui [Upgrading to 5.0](upgrading-to-5.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/5.0.0) - ## 4.x - [Upgrading to 4.0](upgrading-to-4.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/4.0.0) ## 3.0 [Upgrading to 3.0](upgrading-to-3.0.md) · [GitHub Release](https://github.com/rommapp/romm/releases/tag/3.0.0) - ## Want release pings? - Watch the [rommapp/romm](https://github.com/rommapp/romm) repo on GitHub: "Custom → Releases" for release-only notifications. diff --git a/docs/releases/index.md b/docs/releases/index.md index 8e6b0655..fb325a77 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -19,13 +19,13 @@ RomM follows **SemVer** for breaking changes and **CalVer-ish** for cadence: maj ## Image tags and what to pin -| Tag | What it moves to | When to use | -| --- | --- | --- | -| `rommapp/romm:latest` | Every new stable release | Dev / "I'll deal with it" setups. **Never pin production to `:latest`** because you'll ship untested upgrades. | -| `rommapp/romm:5.0.0` | Immutable, specific release | Production. Update deliberately by bumping the tag. | -| `rommapp/romm:5` | Latest in the 5.x line | Middle ground: auto-minor-upgrades within a major. | -| `rommapp/romm:develop` | Every push to `master` | Don't. | -| `rommapp/romm:5.0.0-slim` | Same as `5.0.0` but without EmulatorJS/Ruffle | Headless / API-only deployments. See [Image Variants](../install/image-variants.md). | +| Tag | What it moves to | When to use | +| ------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `rommapp/romm:latest` | Every new stable release | Dev / "I'll deal with it" setups. **Never pin production to `:latest`** because you'll ship untested upgrades. | +| `rommapp/romm:5.0.0` | Immutable, specific release | Production. Update deliberately by bumping the tag. | +| `rommapp/romm:5` | Latest in the 5.x line | Middle ground: auto-minor-upgrades within a major. | +| `rommapp/romm:develop` | Every push to `master` | Don't. | +| `rommapp/romm:5.0.0-slim` | Same as `5.0.0` but without EmulatorJS/Ruffle | Headless / API-only deployments. See [Image Variants](../install/image-variants.md). | Registries: Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/rommapp/romm`), same tags, same content. @@ -41,12 +41,12 @@ The docs site is versioned via [mike](https://github.com/jimporter/mike). Every We support the current major and the previous major for critical bug fixes and security patches. -| Major | Status | Frozen docs | -| --- | --- | --- | -| **5.x** | **Current**, active development | `docs.romm.app/latest/` | -| **4.x** | Security + critical bugs only | `docs.romm.app/4.8/` | -| **3.x** | Unsupported, upgrade | N/A | -| **≤2.x** | Unsupported | N/A | +| Major | Status | Frozen docs | +| -------- | ------------------------------- | ----------------------- | +| **5.x** | **Current**, active development | `docs.romm.app/latest/` | +| **4.x** | Security + critical bugs only | `docs.romm.app/4.8/` | +| **3.x** | Unsupported, upgrade | N/A | +| **≤2.x** | Unsupported | N/A | Older frozen docs are retained for 12 months after the major's support window ends, then removed. Plan upgrades accordingly. diff --git a/docs/resources/snippets/env-vars.md b/docs/resources/snippets/env-vars.md index 0568f385..f6e95a04 100644 --- a/docs/resources/snippets/env-vars.md +++ b/docs/resources/snippets/env-vars.md @@ -2,186 +2,186 @@ ### Core Application -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `ROMM_BASE_PATH` | `/romm` | | Base folder path for library, resources and assets | -| `ROMM_TMP_PATH` | | | Custom temporary directory path | -| `ROMM_BASE_URL` | `http://0.0.0.0` | | Base URL used when rendering container log links | -| `ROMM_PORT` | `8080` | | Port on which the application listens | -| `KIOSK_MODE` | `false` | | Read-only mode for public displays or kiosks | +| Variable | Default | Required | Description | +| ---------------- | ---------------- | :------: | -------------------------------------------------- | +| `ROMM_BASE_PATH` | `/romm` | | Base folder path for library, resources and assets | +| `ROMM_TMP_PATH` | | | Custom temporary directory path | +| `ROMM_BASE_URL` | `http://0.0.0.0` | | Base URL used when rendering container log links | +| `ROMM_PORT` | `8080` | | Port on which the application listens | +| `KIOSK_MODE` | `false` | | Read-only mode for public displays or kiosks | ### Database -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `ROMM_DB_DRIVER` | `mariadb` | | Database driver to use (mariadb, mysql, postgresql) | -| `DB_HOST` | | `✓` | Host name of the database instance | -| `DB_PORT` | `3306` | | Port number of the database instance | -| `DB_NAME` | `romm` | | Database name (should match MYSQL_DATABASE in MariaDB) | -| `DB_USER` | | `✓` | Database username (should match MARIADB_USER in MariaDB) | -| `DB_PASSWD` | | `✓` | Database password (should match MARIADB_PASSWORD in MariaDB) | -| `DB_ROOT_PASSWD` | | | Database root user password (only used by the bundled MariaDB container) | -| `DB_QUERY_JSON` | | | Extra query parameters for the database connection, as JSON | +| Variable | Default | Required | Description | +| ---------------- | --------- | :------: | ------------------------------------------------------------------------ | +| `ROMM_DB_DRIVER` | `mariadb` | | Database driver to use (mariadb, mysql, postgresql) | +| `DB_HOST` | | `✓` | Host name of the database instance | +| `DB_PORT` | `3306` | | Port number of the database instance | +| `DB_NAME` | `romm` | | Database name (should match MYSQL_DATABASE in MariaDB) | +| `DB_USER` | | `✓` | Database username (should match MARIADB_USER in MariaDB) | +| `DB_PASSWD` | | `✓` | Database password (should match MARIADB_PASSWORD in MariaDB) | +| `DB_ROOT_PASSWD` | | | Database root user password (only used by the bundled MariaDB container) | +| `DB_QUERY_JSON` | | | Extra query parameters for the database connection, as JSON | ### Redis/Valkey -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `REDIS_HOST` | `127.0.0.1` | | Host name of the Redis/Valkey instance | -| `REDIS_PORT` | `6379` | | Port number of the Redis/Valkey instance | -| `REDIS_USERNAME` | | | Username for the Redis/Valkey instance | -| `REDIS_PASSWORD` | | | Password for the Redis/Valkey instance | -| `REDIS_DB` | `0` | | Database number for the Redis/Valkey instance | -| `REDIS_SSL` | `false` | | Enable SSL (rediss://) for the Redis/Valkey connection | +| Variable | Default | Required | Description | +| ---------------- | ----------- | :------: | ------------------------------------------------------ | +| `REDIS_HOST` | `127.0.0.1` | | Host name of the Redis/Valkey instance | +| `REDIS_PORT` | `6379` | | Port number of the Redis/Valkey instance | +| `REDIS_USERNAME` | | | Username for the Redis/Valkey instance | +| `REDIS_PASSWORD` | | | Password for the Redis/Valkey instance | +| `REDIS_DB` | `0` | | Database number for the Redis/Valkey instance | +| `REDIS_SSL` | `false` | | Enable SSL (rediss://) for the Redis/Valkey connection | ### Authentication -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `ROMM_AUTH_SECRET_KEY` | | `✓` | App secret, generate with `openssl rand -hex 32` | -| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | `1800` | | Access token lifetime in seconds | -| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | `604800` | | Refresh token lifetime in seconds | -| `SESSION_MAX_AGE_SECONDS` | `1209600` | | Maximum age of a session in seconds | -| `INVITE_TOKEN_EXPIRY_SECONDS` | `600` | | Invite token lifetime in seconds | -| `DISABLE_DOWNLOAD_ENDPOINT_AUTH` | `false` | | Disable auth on the download endpoint for WebRcade/Tinfoil | -| `DISABLE_CSRF_PROTECTION` | `false` | | Disable CSRF protection (not recommended) | -| `DISABLE_USERPASS_LOGIN` | `false` | | Disable username/password login when using OIDC | -| `DISABLE_SETUP_WIZARD` | `false` | | Skip the first-boot setup wizard | +| Variable | Default | Required | Description | +| ------------------------------------ | --------- | :------: | ---------------------------------------------------------- | +| `ROMM_AUTH_SECRET_KEY` | | `✓` | App secret, generate with `openssl rand -hex 32` | +| `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | `1800` | | Access token lifetime in seconds | +| `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | `604800` | | Refresh token lifetime in seconds | +| `SESSION_MAX_AGE_SECONDS` | `1209600` | | Maximum age of a session in seconds | +| `INVITE_TOKEN_EXPIRY_SECONDS` | `600` | | Invite token lifetime in seconds | +| `DISABLE_DOWNLOAD_ENDPOINT_AUTH` | `false` | | Disable auth on the download endpoint for WebRcade/Tinfoil | +| `DISABLE_CSRF_PROTECTION` | `false` | | Disable CSRF protection (not recommended) | +| `DISABLE_USERPASS_LOGIN` | `false` | | Disable username/password login when using OIDC | +| `DISABLE_SETUP_WIZARD` | `false` | | Skip the first-boot setup wizard | ### OpenID Connect -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `OIDC_ENABLED` | `false` | | Enable OpenID Connect authentication | -| `OIDC_AUTOLOGIN` | `false` | | Skip the OIDC button on the login page and auto-redirect | -| `OIDC_PROVIDER` | | | Name of the OIDC provider in use | -| `OIDC_CLIENT_ID` | | | Client ID for OIDC authentication | -| `OIDC_CLIENT_SECRET` | | | Client secret for OIDC authentication | -| `OIDC_REDIRECT_URI` | | | Absolute redirect URI for OIDC authentication | -| `OIDC_SERVER_APPLICATION_URL` | | | Absolute URL of the OIDC server application | -| `OIDC_SERVER_METADATA_URL` | | | URL to the OIDC provider metadata endpoint | -| `OIDC_CLAIM_ROLES` | | | OIDC claim containing user roles | -| `OIDC_ROLE_VIEWER` | | | Role value mapping to viewer permissions | -| `OIDC_ROLE_EDITOR` | | | Role value mapping to editor permissions | -| `OIDC_ROLE_ADMIN` | | | Role value mapping to admin permissions | -| `OIDC_TLS_CACERTFILE` | | | Path to file containing trusted CA certificates | -| `OIDC_USERNAME_ATTRIBUTE` | `preferred_username` | | Attribute on OIDC user info used as the username | -| `OIDC_RP_INITIATED_LOGOUT` | `false` | | Enable RP-initiated logout flow | -| `OIDC_END_SESSION_ENDPOINT` | | | OIDC end-session endpoint override URL | +| Variable | Default | Required | Description | +| ----------------------------- | -------------------- | :------: | -------------------------------------------------------- | +| `OIDC_ENABLED` | `false` | | Enable OpenID Connect authentication | +| `OIDC_AUTOLOGIN` | `false` | | Skip the OIDC button on the login page and auto-redirect | +| `OIDC_PROVIDER` | | | Name of the OIDC provider in use | +| `OIDC_CLIENT_ID` | | | Client ID for OIDC authentication | +| `OIDC_CLIENT_SECRET` | | | Client secret for OIDC authentication | +| `OIDC_REDIRECT_URI` | | | Absolute redirect URI for OIDC authentication | +| `OIDC_SERVER_APPLICATION_URL` | | | Absolute URL of the OIDC server application | +| `OIDC_SERVER_METADATA_URL` | | | URL to the OIDC provider metadata endpoint | +| `OIDC_CLAIM_ROLES` | | | OIDC claim containing user roles | +| `OIDC_ROLE_VIEWER` | | | Role value mapping to viewer permissions | +| `OIDC_ROLE_EDITOR` | | | Role value mapping to editor permissions | +| `OIDC_ROLE_ADMIN` | | | Role value mapping to admin permissions | +| `OIDC_TLS_CACERTFILE` | | | Path to file containing trusted CA certificates | +| `OIDC_USERNAME_ATTRIBUTE` | `preferred_username` | | Attribute on OIDC user info used as the username | +| `OIDC_RP_INITIATED_LOGOUT` | `false` | | Enable RP-initiated logout flow | +| `OIDC_END_SESSION_ENDPOINT` | | | OIDC end-session endpoint override URL | ### Metadata Providers -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `IGDB_CLIENT_ID` | | | Client ID for the IGDB API | -| `IGDB_CLIENT_SECRET` | | | Client secret for the IGDB API | -| `MOBYGAMES_API_KEY` | | | MobyGames secret API key | -| `SCREENSCRAPER_USER` | | | Screenscraper username | -| `SCREENSCRAPER_PASSWORD` | | | Screenscraper password | -| `STEAMGRIDDB_API_KEY` | | | SteamGridDB secret API key | -| `RETROACHIEVEMENTS_API_KEY` | | | RetroAchievements secret API key | -| `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` | `30` | | RetroAchievements metadata cache refresh interval in days | -| `PLAYMATCH_API_ENABLED` | `false` | | Enable PlayMatch API integration | -| `LAUNCHBOX_API_ENABLED` | `false` | | Enable LaunchBox API integration | -| `HASHEOUS_API_ENABLED` | `false` | | Enable Hasheous API integration | -| `FLASHPOINT_API_ENABLED` | `false` | | Enable Flashpoint API integration | -| `HLTB_API_ENABLED` | `false` | | Enable HowLongToBeat API integration | -| `TGDB_API_ENABLED` | `false` | | Enable TheGamesDB API integration | +| Variable | Default | Required | Description | +| -------------------------------------- | ------- | :------: | --------------------------------------------------------- | +| `IGDB_CLIENT_ID` | | | Client ID for the IGDB API | +| `IGDB_CLIENT_SECRET` | | | Client secret for the IGDB API | +| `MOBYGAMES_API_KEY` | | | MobyGames secret API key | +| `SCREENSCRAPER_USER` | | | Screenscraper username | +| `SCREENSCRAPER_PASSWORD` | | | Screenscraper password | +| `STEAMGRIDDB_API_KEY` | | | SteamGridDB secret API key | +| `RETROACHIEVEMENTS_API_KEY` | | | RetroAchievements secret API key | +| `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` | `30` | | RetroAchievements metadata cache refresh interval in days | +| `PLAYMATCH_API_ENABLED` | `false` | | Enable PlayMatch API integration | +| `LAUNCHBOX_API_ENABLED` | `false` | | Enable LaunchBox API integration | +| `HASHEOUS_API_ENABLED` | `false` | | Enable Hasheous API integration | +| `FLASHPOINT_API_ENABLED` | `false` | | Enable Flashpoint API integration | +| `HLTB_API_ENABLED` | `false` | | Enable HowLongToBeat API integration | +| `TGDB_API_ENABLED` | `false` | | Enable TheGamesDB API integration | ### Scans & Tasks -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `SCAN_TIMEOUT` | `14400` | | Timeout for background scan/rescan tasks in seconds | -| `SCAN_WORKERS` | `1` | | Number of worker processes for scanning tasks | -| `TASK_TIMEOUT` | `300` | | Timeout for other background tasks in seconds | -| `TASK_RESULT_TTL` | `86400` | | How long to keep task results in Valkey in seconds | -| `SEVEN_ZIP_TIMEOUT` | `60` | | Timeout for 7-Zip operations in seconds | -| `ENABLE_RESCAN_ON_FILESYSTEM_CHANGE` | `false` | | Re-scan the library automatically when the filesystem changes | -| `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` | `5` | | Delay in minutes before re-scanning after a filesystem change | -| `ENABLE_SCHEDULED_RESCAN` | `false` | | Enable scheduled library re-scans | -| `SCHEDULED_RESCAN_CRON` | `0 3 * * *` | | Cron expression for scheduled re-scans | -| `ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB` | `false` | | Enable scheduled Switch TitleDB index updates | -| `SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON` | `0 4 * * *` | | Cron expression for scheduled Switch TitleDB updates | -| `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA` | `false` | | Enable scheduled LaunchBox metadata updates | -| `SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON` | `0 4 * * *` | | Cron expression for scheduled LaunchBox metadata updates | -| `ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP` | `false` | | Enable scheduled conversion of images to WebP | -| `SCHEDULED_CONVERT_IMAGES_TO_WEBP_CRON` | `0 4 * * *` | | Cron expression for scheduled WebP conversion | -| `ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC` | `false` | | Enable scheduled RetroAchievements progress sync | -| `SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC_CRON` | `0 4 * * *` | | Cron expression for scheduled RetroAchievements sync | +| Variable | Default | Required | Description | +| -------------------------------------------------- | ----------- | :------: | ------------------------------------------------------------- | +| `SCAN_TIMEOUT` | `14400` | | Timeout for background scan/rescan tasks in seconds | +| `SCAN_WORKERS` | `1` | | Number of worker processes for scanning tasks | +| `TASK_TIMEOUT` | `300` | | Timeout for other background tasks in seconds | +| `TASK_RESULT_TTL` | `86400` | | How long to keep task results in Valkey in seconds | +| `SEVEN_ZIP_TIMEOUT` | `60` | | Timeout for 7-Zip operations in seconds | +| `ENABLE_RESCAN_ON_FILESYSTEM_CHANGE` | `false` | | Re-scan the library automatically when the filesystem changes | +| `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` | `5` | | Delay in minutes before re-scanning after a filesystem change | +| `ENABLE_SCHEDULED_RESCAN` | `false` | | Enable scheduled library re-scans | +| `SCHEDULED_RESCAN_CRON` | `0 3 * * *` | | Cron expression for scheduled re-scans | +| `ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB` | `false` | | Enable scheduled Switch TitleDB index updates | +| `SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON` | `0 4 * * *` | | Cron expression for scheduled Switch TitleDB updates | +| `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA` | `false` | | Enable scheduled LaunchBox metadata updates | +| `SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON` | `0 4 * * *` | | Cron expression for scheduled LaunchBox metadata updates | +| `ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP` | `false` | | Enable scheduled conversion of images to WebP | +| `SCHEDULED_CONVERT_IMAGES_TO_WEBP_CRON` | `0 4 * * *` | | Cron expression for scheduled WebP conversion | +| `ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC` | `false` | | Enable scheduled RetroAchievements progress sync | +| `SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC_CRON` | `0 4 * * *` | | Cron expression for scheduled RetroAchievements sync | ### Sync -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `SYNC_BASE_PATH` | | | Base folder for sync state (defaults to $ROMM_BASE_PATH/sync) | -| `ENABLE_SYNC_FOLDER_WATCHER` | `false` | | Watch the sync folder and trigger scans on change | -| `SYNC_FOLDER_SCAN_DELAY` | `2` | | Delay in minutes before scanning after a sync folder change | -| `ENABLE_SYNC_PUSH_PULL` | `false` | | Enable scheduled sync push/pull | -| `SYNC_PUSH_PULL_CRON` | `*/30 * * * *` | | Cron expression for scheduled sync push/pull | -| `SYNC_SSH_KEYS_PATH` | | | Path to SSH keys for sync remotes (defaults to $ROMM_BASE_PATH/sync/keys) | -| `SYNC_SSH_KNOWN_HOSTS_PATH` | | | Path to SSH known_hosts (defaults to $ROMM_BASE_PATH/sync/known_hosts) | +| Variable | Default | Required | Description | +| ---------------------------- | -------------- | :------: | ------------------------------------------------------------------------- | +| `SYNC_BASE_PATH` | | | Base folder for sync state (defaults to $ROMM_BASE_PATH/sync) | +| `ENABLE_SYNC_FOLDER_WATCHER` | `false` | | Watch the sync folder and trigger scans on change | +| `SYNC_FOLDER_SCAN_DELAY` | `2` | | Delay in minutes before scanning after a sync folder change | +| `ENABLE_SYNC_PUSH_PULL` | `false` | | Enable scheduled sync push/pull | +| `SYNC_PUSH_PULL_CRON` | `*/30 * * * *` | | Cron expression for scheduled sync push/pull | +| `SYNC_SSH_KEYS_PATH` | | | Path to SSH keys for sync remotes (defaults to $ROMM_BASE_PATH/sync/keys) | +| `SYNC_SSH_KNOWN_HOSTS_PATH` | | | Path to SSH known_hosts (defaults to $ROMM_BASE_PATH/sync/known_hosts) | ### Emulation -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `DISABLE_EMULATOR_JS` | `false` | | Disable in-browser play via EmulatorJS | -| `DISABLE_RUFFLE_RS` | `false` | | Disable in-browser Flash playback via RuffleRS | +| Variable | Default | Required | Description | +| --------------------- | ------- | :------: | ---------------------------------------------- | +| `DISABLE_EMULATOR_JS` | `false` | | Disable in-browser play via EmulatorJS | +| `DISABLE_RUFFLE_RS` | `false` | | Disable in-browser Flash playback via RuffleRS | ### Integrations -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `YOUTUBE_BASE_URL` | `https://www.youtube.com` | | Base URL for alternate YouTube frontends (Piped, Invidious, etc.) | -| `TINFOIL_WELCOME_MESSAGE` | `RomM Switch Library` | | Welcome message shown in Tinfoil Switch clients | +| Variable | Default | Required | Description | +| ------------------------- | ------------------------- | :------: | ----------------------------------------------------------------- | +| `YOUTUBE_BASE_URL` | `https://www.youtube.com` | | Base URL for alternate YouTube frontends (Piped, Invidious, etc.) | +| `TINFOIL_WELCOME_MESSAGE` | `RomM Switch Library` | | Welcome message shown in Tinfoil Switch clients | ### Logging -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `LOGLEVEL` | `INFO` | | Application log level | -| `FORCE_COLOR` | `false` | | Force colored log output | -| `NO_COLOR` | `false` | | Disable colored log output | +| Variable | Default | Required | Description | +| ------------- | ------- | :------: | -------------------------- | +| `LOGLEVEL` | `INFO` | | Application log level | +| `FORCE_COLOR` | `false` | | Force colored log output | +| `NO_COLOR` | `false` | | Disable colored log output | ### Web Server -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `WEB_SERVER_CONCURRENCY` | `1` | | Number of worker processes (recommended: 2 × CPU cores + 1) | -| `WEB_SERVER_TIMEOUT` | `300` | | Timeout for web server requests in seconds | -| `WEB_SERVER_KEEPALIVE` | `2` | | Keep-Alive connection wait time in seconds | -| `WEB_SERVER_MAX_REQUESTS` | `1000` | | Maximum requests a worker processes before restarting | -| `WEB_SERVER_MAX_REQUESTS_JITTER` | `100` | | Random jitter added to max requests value | -| `WEB_SERVER_WORKER_CONNECTIONS` | `1000` | | Maximum simultaneous clients per worker process | -| `WEB_SERVER_GUNICORN_WAIT_SECONDS` | `30` | | Seconds to wait for Gunicorn to start before giving up | -| `IPV4_ONLY` | `false` | | Bind only to IPv4 | +| Variable | Default | Required | Description | +| ---------------------------------- | ------- | :------: | ----------------------------------------------------------- | +| `WEB_SERVER_CONCURRENCY` | `1` | | Number of worker processes (recommended: 2 × CPU cores + 1) | +| `WEB_SERVER_TIMEOUT` | `300` | | Timeout for web server requests in seconds | +| `WEB_SERVER_KEEPALIVE` | `2` | | Keep-Alive connection wait time in seconds | +| `WEB_SERVER_MAX_REQUESTS` | `1000` | | Maximum requests a worker processes before restarting | +| `WEB_SERVER_MAX_REQUESTS_JITTER` | `100` | | Random jitter added to max requests value | +| `WEB_SERVER_WORKER_CONNECTIONS` | `1000` | | Maximum simultaneous clients per worker process | +| `WEB_SERVER_GUNICORN_WAIT_SECONDS` | `30` | | Seconds to wait for Gunicorn to start before giving up | +| `IPV4_ONLY` | `false` | | Bind only to IPv4 | ### Proxy -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `HTTP_PROXY` | | | HTTP proxy URL for outbound requests | -| `HTTPS_PROXY` | | | HTTPS proxy URL for outbound requests | -| `NO_PROXY` | | | Comma-separated list of hosts to bypass the proxy | +| Variable | Default | Required | Description | +| ------------- | ------- | :------: | ------------------------------------------------- | +| `HTTP_PROXY` | | | HTTP proxy URL for outbound requests | +| `HTTPS_PROXY` | | | HTTPS proxy URL for outbound requests | +| `NO_PROXY` | | | Comma-separated list of hosts to bypass the proxy | ### Observability -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `SENTRY_DSN` | | | DSN for Sentry error tracking | +| Variable | Default | Required | Description | +| ------------ | ------- | :------: | ----------------------------- | +| `SENTRY_DSN` | | | DSN for Sentry error tracking | ### Development -| Variable | Default | Required | Description | -| --- | --- | :---: | --- | -| `DEV_MODE` | `false` | | Enable development mode (debugging, hot-reloading) | -| `DEV_HOST` | `127.0.0.1` | | Host for the development server | -| `DEV_PORT` | `5000` | | Port for the development server | -| `DEV_HTTPS` | `false` | | Enable HTTPS in the development server | -| `DEV_SQL_ECHO` | `false` | | Log all SQL queries in development mode | -| `POSTGRES_DB` | `authentik` | | Postgres database name for the Authentik dev stack | -| `POSTGRES_USER` | `authentik` | | Postgres user for the Authentik dev stack | -| `POSTGRES_PASSWORD` | `authentik` | | Postgres password for the Authentik dev stack | -| `AUTHENTIK_SECRET_KEY` | | | Authentik secret key | -| `AUTHENTIK_BOOTSTRAP_PASSWORD` | | | Initial Authentik admin bootstrap password | +| Variable | Default | Required | Description | +| ------------------------------ | ----------- | :------: | -------------------------------------------------- | +| `DEV_MODE` | `false` | | Enable development mode (debugging, hot-reloading) | +| `DEV_HOST` | `127.0.0.1` | | Host for the development server | +| `DEV_PORT` | `5000` | | Port for the development server | +| `DEV_HTTPS` | `false` | | Enable HTTPS in the development server | +| `DEV_SQL_ECHO` | `false` | | Log all SQL queries in development mode | +| `POSTGRES_DB` | `authentik` | | Postgres database name for the Authentik dev stack | +| `POSTGRES_USER` | `authentik` | | Postgres user for the Authentik dev stack | +| `POSTGRES_PASSWORD` | `authentik` | | Postgres password for the Authentik dev stack | +| `AUTHENTIK_SECRET_KEY` | | | Authentik secret key | +| `AUTHENTIK_BOOTSTRAP_PASSWORD` | | | Initial Authentik admin bootstrap password | diff --git a/docs/resources/snippets/scheduled-tasks.md b/docs/resources/snippets/scheduled-tasks.md index 1c72055a..c4c5738a 100644 --- a/docs/resources/snippets/scheduled-tasks.md +++ b/docs/resources/snippets/scheduled-tasks.md @@ -1,15 +1,15 @@ -| Task | Type | Default schedule | Env var | Purpose | -| --- | --- | --- | --- | --- | -| Folder Scan | Scheduled | `0 0 * * *` | `SCAN_INTERVAL_CRON` | Rescan ROM library for new or changed files. | -| Switch titleDB Fetch | Scheduled | `0 12 * * 0` | `SWITCH_TITLEDB_FETCH_INTERVAL_CRON` | Update Nintendo Switch game database used for matching. | -| LaunchBox Metadata Sync | Scheduled | `0 2 * * *` | `LAUNCHBOX_SYNC_INTERVAL_CRON` | Synchronize LaunchBox metadata cache. | -| Image Conversion | Scheduled | `0 3 * * *` | `IMAGE_CONVERSION_INTERVAL_CRON` | Convert media to WebP for serving. | -| RetroAchievements Sync | Scheduled | `0 4 * * *` | `RETROACHIEVEMENTS_SYNC_INTERVAL_CRON` | Sync per-user RetroAchievements progression data. | -| Netplay Cleanup | Scheduled | `*/30 * * * *` | `NETPLAY_CLEANUP_INTERVAL_CRON` | Remove orphaned netplay sessions. | -| Push-Pull Device Sync | Scheduled | `*/15 * * * *` | `PUSH_PULL_SYNC_INTERVAL_CRON` | Bidirectional save/state sync to registered devices. | -| Cleanup Missing ROMs | Manual | `-` | `-` | Remove DB entries whose files are no longer on disk. | -| Cleanup Orphaned Resources | Manual | `-` | `-` | Delete cached media not referenced by any ROM. | -| Sync Folder Scan | Manual | `-` | `-` | On-demand library scan + sync. | -| Filesystem Watcher | Watcher | `-` | `WATCHER_ENABLED` | Live-watch the library folder and trigger quick scans on changes. | +| Task | Type | Default schedule | Env var | Purpose | +| -------------------------- | --------- | ---------------- | -------------------------------------- | ----------------------------------------------------------------- | +| Folder Scan | Scheduled | `0 0 * * *` | `SCAN_INTERVAL_CRON` | Rescan ROM library for new or changed files. | +| Switch titleDB Fetch | Scheduled | `0 12 * * 0` | `SWITCH_TITLEDB_FETCH_INTERVAL_CRON` | Update Nintendo Switch game database used for matching. | +| LaunchBox Metadata Sync | Scheduled | `0 2 * * *` | `LAUNCHBOX_SYNC_INTERVAL_CRON` | Synchronize LaunchBox metadata cache. | +| Image Conversion | Scheduled | `0 3 * * *` | `IMAGE_CONVERSION_INTERVAL_CRON` | Convert media to WebP for serving. | +| RetroAchievements Sync | Scheduled | `0 4 * * *` | `RETROACHIEVEMENTS_SYNC_INTERVAL_CRON` | Sync per-user RetroAchievements progression data. | +| Netplay Cleanup | Scheduled | `*/30 * * * *` | `NETPLAY_CLEANUP_INTERVAL_CRON` | Remove orphaned netplay sessions. | +| Push-Pull Device Sync | Scheduled | `*/15 * * * *` | `PUSH_PULL_SYNC_INTERVAL_CRON` | Bidirectional save/state sync to registered devices. | +| Cleanup Missing ROMs | Manual | `-` | `-` | Remove DB entries whose files are no longer on disk. | +| Cleanup Orphaned Resources | Manual | `-` | `-` | Delete cached media not referenced by any ROM. | +| Sync Folder Scan | Manual | `-` | `-` | On-demand library scan + sync. | +| Filesystem Watcher | Watcher | `-` | `WATCHER_ENABLED` | Live-watch the library folder and trigger quick scans on changes. | diff --git a/docs/resources/snippets/supported-platforms.md b/docs/resources/snippets/supported-platforms.md index 16a2894f..a0a69ab4 100644 --- a/docs/resources/snippets/supported-platforms.md +++ b/docs/resources/snippets/supported-platforms.md @@ -1,462 +1,462 @@ -|Platform Name|Folder Name|Metadata Providers| -|---|---|---| -| 1292 Advanced Programmable Video System | `1292-advanced-programmable-video-system` | igdb logo mobygames logo | -| 3DO Interactive Multiplayer | `3do` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| 8-Bit Productions Commander X16 | `commander-x16` | hasheous logo | -| Aamber Pegasus | `pegasus` | screenscraper logo launchbox logo | -| ABC 80 | `abc-80` | mobygames logo | -| Acorn Archimedes | `acorn-archimedes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Acorn Electron | `acorn-electron` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Action Max | `action-max` | screenscraper logo launchbox logo hasheous logo | -| Advanced Pico Beena | `advanced-pico-beena` | igdb logo | -| Adventure Vision | `adventure-vision` | screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | -| AirConsole | `airconsole` | igdb logo mobygames logo | -| Alice 32/90 | `alice-3290` | mobygames logo launchbox logo | -| Altair 680 | `altair-680` | mobygames logo | -| Altair 8800 | `altair-8800` | mobygames logo hasheous logo | -| Amazon Alexa | `amazon-alexa` | mobygames logo | -| Amazon Fire TV | `amazon-fire-tv` | igdb logo mobygames logo | -| Amiga | `amiga` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Amiga CD | `amiga-cd` | screenscraper logo | -| Amiga CD32 | `amiga-cd32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Amstrad CPC | `acpc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Amstrad GX4000 | `amstrad-gx4000` | igdb logo screenscraper logo launchbox logo hasheous logo | -| Amstrad PCW | `amstrad-pcw` | igdb logo mobygames logo hasheous logo | -| Analogue electronics | `analogueelectronics` | igdb logo | -| Android | `android` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Antstream | `antstream` | mobygames logo | -| APF MP1000/Imagination Machine | `apf` | mobygames logo launchbox logo hasheous logo | -| Apogee BK-01 | `bk-01` | launchbox logo | -| Apple I | `apple` | screenscraper logo mobygames logo hasheous logo | -| Apple II | `appleii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Apple IIGS | `apple-iigs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Apple III | `appleiii` | screenscraper logo hasheous logo | -| Apple Lisa | `apple-lisa` | hasheous logo | -| Apple Pippin | `apple-pippin` | igdb logo hasheous logo | -| Arcade | `arcade` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Arcadia 2001 | `arcadia-2001` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo | -| Arduboy | `arduboy` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | -| Astral 2000 | `astral-2000` | mobygames logo | -| Atari 2600 | `atari2600` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Atari 5200 | `atari5200` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Atari 7800 | `atari7800` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Atari 8-bit | `atari8bit` | igdb logo screenscraper logo mobygames logo hasheous logo howlongtobeat logo | -| Atari 800 | `atari800` | screenscraper logo launchbox logo | -| Atari Jaguar | `jaguar` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Atari Jaguar CD | `atari-jaguar-cd` | igdb logo screenscraper logo launchbox logo retroachivements logo howlongtobeat logo | -| Atari Lynx | `lynx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Atari ST/STE | `atari-st` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Atari VCS | `atari-vcs` | mobygames logo | -| Atari XEGS | `atari-xegs` | screenscraper logo launchbox logo | -| Atom | `atom` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| AY-3-8500 | `ay-3-8500` | igdb logo | -| AY-3-8603 | `ay-3-8603` | igdb logo | -| AY-3-8605 | `ay-3-8605` | igdb logo | -| AY-3-8606 | `ay-3-8606` | igdb logo | -| AY-3-8607 | `ay-3-8607` | igdb logo | -| AY-3-8610 | `ay-3-8610` | igdb logo | -| AY-3-8710 | `ay-3-8710` | igdb logo | -| AY-3-8760 | `ay-3-8760` | igdb logo | -| Bada | `bada` | mobygames logo | -| Bally Astrocade | `astrocade` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| BBC Microcomputer System | `bbcmicro` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Benesse Pocket Challenge V2 | `pocket-challenge-v2` | screenscraper logo hasheous logo | -| Benesse Pocket Challenge W | `pocket-challenge-w` | hasheous logo | -| BeOS | `beos` | mobygames logo | -| BGR Computers Excalibur 64 | `excalibur-64` | hasheous logo | -| Bit Corporation BIT 90 | `bit-90` | hasheous logo | -| Black Point | `black-point` | | -| BlackBerry OS | `blackberry` | igdb logo mobygames logo | -| Blacknut | `blacknut` | mobygames logo | -| Blu-ray Player | `blu-ray-player` | igdb logo mobygames logo | -| BREW | `brew` | mobygames logo | -| Browser (Flash/HTML5) | `browser` | igdb logo mobygames logo launchbox logo flashpoint logo howlongtobeat logo | -| Bubble | `bubble` | mobygames logo | -| Call-A-Computer time-shared mainframe computer system | `call-a-computer` | igdb logo | -| Cambridge Computer Z88 | `z88` | hasheous logo | -| Camputers Lynx | `camputers-lynx` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| Capcom Play System | `cps1` | screenscraper logo | -| Capcom Play System 2 | `cps2` | screenscraper logo | -| Capcom Play System 3 | `cps3` | screenscraper logo | -| Casio CFX-9850 | `casio-cfx-9850` | hasheous logo | -| Casio FP-1000 & FP-1100 | `casio-fp-1000` | hasheous logo | -| Casio Loopy | `casio-loopy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Casio PB-1000 | `casio-pb-1000` | hasheous logo | -| Casio Programmable Calculator | `casio-programmable-calculator` | mobygames logo | -| Casio PV-1000 | `casio-pv-1000` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| Casio PV-2000 | `casio-pv-2000` | hasheous logo | -| CDC Cyber 70 | `cdccyber70` | igdb logo | -| Champion 2711 | `champion-2711` | mobygames logo | -| ClickStart | `clickstart` | mobygames logo | -| Coleco Adam | `colecoadam` | screenscraper logo mobygames logo launchbox logo | -| ColecoVision | `colecovision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Colour Genie | `colour-genie` | screenscraper logo mobygames logo launchbox logo | -| Commodore 128 | `c128` | screenscraper logo mobygames logo launchbox logo hasheous logo | -| Commodore 16 | `c16` | igdb logo screenscraper logo mobygames logo hasheous logo | -| Commodore C64/128/MAX | `c64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Commodore CDTV | `commodore-cdtv` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| Commodore PET | `cpet` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Commodore Plus/4 | `c-plus-4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Commodore VIC-20 | `vic-20` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | -| Compal 80 | `compal-80` | mobygames logo | -| Compucolor I | `compucolor-i` | mobygames logo | -| Compucolor II | `compucolor-ii` | mobygames logo | -| Compucorp Programmable Calculator | `compucorp-programmable-calculator` | mobygames logo | -| COSMAC | `fred-cosmac` | mobygames logo | -| CP/M | `cpm` | mobygames logo | -| CreatiVision | `creativision` | screenscraper logo mobygames logo launchbox logo | -| Cybervision | `cybervision` | mobygames logo | -| Danger OS | `danger-os` | mobygames logo | -| Daydream | `daydream` | igdb logo | -| DEC GT40 | `gt40` | igdb logo | -| Dedicated console | `dedicated-console` | mobygames logo | -| Dedicated handheld | `dedicated-handheld` | mobygames logo | -| Didj | `didj` | mobygames logo | -| Digiblast | `digiblast` | igdb logo mobygames logo | -| DoJa | `doja` | mobygames logo | -| Donner Model 30 | `donner30` | igdb logo | -| DOS | `dos` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | -| Dragon 32/64 | `dragon-32-slash-64` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Dreamcast | `dc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| DVD Player | `dvd-player` | igdb logo mobygames logo howlongtobeat logo | -| e-Reader / Card-e Reader | `e-reader-slash-card-e-reader` | igdb logo | -| ECD Micromind | `ecd-micromind` | mobygames logo | -| EDSAC | `edsac` | igdb logo | -| Elektor TV Games Computer | `elektor` | igdb logo retroachivements logo | -| Elektronika BK | `bk` | screenscraper logo launchbox logo | -| Enterprise | `enterprise` | mobygames logo launchbox logo | -| Epoch Cassette Vision | `epoch-cassette-vision` | igdb logo mobygames logo | -| Epoch Game Pocket Computer | `epoch-game-pocket-computer` | screenscraper logo mobygames logo launchbox logo | -| Epoch Super Cassette Vision | `epoch-super-cassette-vision` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Evercade | `evercade` | igdb logo mobygames logo howlongtobeat logo | -| Exelvision | `exelvision` | screenscraper logo mobygames logo launchbox logo | -| ExEn | `exen` | mobygames logo | -| Exidy Sorcerer | `exidy-sorcerer` | igdb logo screenscraper logo mobygames logo launchbox logo | -| Fairchild Channel F | `fairchild-channel-f` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | -| Family Computer | `famicom` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo libretro logo | -| Family Computer Disk System | `fds` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | -| Feature phone | `mobile-custom` | mobygames logo | -| Ferranti Nimrod Computer | `nimrod` | igdb logo | -| FM Towns | `fm-towns` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| FM-7 | `fm-7` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| Freebox | `freebox` | mobygames logo | -| G-cluster | `g-cluster` | mobygames logo | -| Galaksija | `galaksija` | mobygames logo | -| Gamate | `gamate` | igdb logo screenscraper logo hasheous logo | -| Game & Watch | `g-and-w` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| Game Boy | `gb` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Game Boy Advance | `gba` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Game Boy Color | `gbc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Game Master | `hartung` | screenscraper logo launchbox logo | -| Game Wave | `game-wave` | mobygames logo launchbox logo | -| Game.com | `game-dot-com` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| GameStick | `gamestick` | mobygames logo | -| Gear VR | `gear-vr` | igdb logo howlongtobeat logo | -| GIMINI | `gimini` | mobygames logo | -| Gizmondo | `gizmondo` | igdb logo mobygames logo howlongtobeat logo | -| Gloud | `gloud` | mobygames logo | -| Glulx | `glulx` | mobygames logo | -| GNEX | `gnex` | mobygames logo | -| Google Stadia | `stadia` | igdb logo mobygames logo howlongtobeat logo | -| GP2X | `gp2x` | mobygames logo | -| GP2X Wiz | `gp2x-wiz` | mobygames logo | -| GP32 | `gp32` | screenscraper logo mobygames logo launchbox logo | -| GVM | `gvm` | mobygames logo | -| Handheld Electronic LCD | `handheld-electronic-lcd` | igdb logo | -| HD DVD Player | `hd-dvd-player` | mobygames logo | -| Heath/Zenith H8/H89 | `heathzenith` | mobygames logo | -| Heathkit H11 | `heathkit-h11` | mobygames logo | -| Hector HRX | `hrx` | launchbox logo | -| Hitachi S1 | `hitachi-s1` | mobygames logo | -| HP 2100 | `hp2100` | igdb logo | -| HP 3000 | `hp3000` | igdb logo | -| HP 9800 | `hp-9800` | mobygames logo | -| HP Programmable Calculator | `hp-programmable-calculator` | mobygames logo | -| Hugo | `hugo` | mobygames logo | -| Hyper Neo Geo 64 | `hyper-neo-geo-64` | igdb logo | -| HyperScan | `hyperscan` | igdb logo mobygames logo launchbox logo | -| IBM 5100 | `ibm-5100` | mobygames logo | -| IBM PCjr | `pc-jr` | hasheous logo | -| Ideal-Computer | `ideal-computer` | mobygames logo | -| iiRcade | `iircade` | mobygames logo | -| Imlac PDS-1 | `imlac-pds1` | igdb logo | -| Intel 8008 | `intel-8008` | mobygames logo | -| Intel 8080 | `intel-8080` | mobygames logo | -| Intel 8086 / 8088 | `intel-8086` | mobygames logo | -| Intellivision | `intellivision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Intellivision Amico | `intellivision-amico` | igdb logo | -| Interact Model One | `interact-model-one` | mobygames logo | -| Interton VC 4000 | `interton-vc-4000` | retroachivements logo | -| Interton Video 2000 | `interton-video-2000` | mobygames logo | -| iOS | `ios` | igdb logo mobygames logo launchbox logo | -| iPad | `ipad` | mobygames logo | -| iPod Classic | `ipod-classic` | mobygames logo | -| J2ME | `j2me` | mobygames logo | -| Jolt | `jolt` | mobygames logo | -| Jupiter Ace | `jupiter-ace` | screenscraper logo mobygames logo launchbox logo | -| KaiOS | `kaios` | mobygames logo | -| KIM-1 | `kim-1` | mobygames logo | -| Kindle Classic | `kindle` | mobygames logo | -| Laser 200 | `laser200` | mobygames logo | -| LaserActive | `laseractive` | igdb logo mobygames logo | -| LeapFrog Explorer | `leapfrog-explorer` | mobygames logo | -| Leapster | `leapster` | igdb logo mobygames logo | -| Leapster Explorer/LeadPad Explorer | `leapster-explorer-slash-leadpad-explorer` | igdb logo mobygames logo | -| LeapTV | `leaptv` | igdb logo mobygames logo | -| Legacy Computer | `legacy-computer` | igdb logo | -| Legacy Mobile Device | `mobile` | igdb logo howlongtobeat logo | -| Linux | `linux` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Luna | `luna` | mobygames logo howlongtobeat logo | -| Mac | `mac` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Maemo | `maemo` | mobygames logo | -| Magnavox Odyssey | `odyssey` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Mainframe | `mainframe` | mobygames logo | -| Matsushita/Panasonic JR | `matsushitapanasonic-jr` | mobygames logo | -| Mattel Aquarius | `aquarius` | mobygames logo launchbox logo hasheous logo | -| MeeGo | `meego` | mobygames logo | -| Mega Duck/Cougar Boy | `mega-duck-slash-cougar-boy` | igdb logo launchbox logo retroachivements logo | -| Memotech MTX | `memotech-mtx` | mobygames logo | -| Memotech MTX512 | `mtx512` | launchbox logo | -| Meritum | `meritum` | mobygames logo | -| Meta Quest 2 | `meta-quest-2` | igdb logo | -| Meta Quest 3 | `meta-quest-3` | igdb logo | -| Microbee | `microbee` | mobygames logo hasheous logo | -| Microcomputer | `microcomputer` | igdb logo | -| Microsoft MSX2+ | `msx2plus` | screenscraper logo launchbox logo | -| Microtan 65 | `microtan-65` | mobygames logo | -| Microvision | `microvision` | igdb logo mobygames logo | -| Mophun | `mophun` | mobygames logo | -| MOS Technology 6502 | `mos-technology-6502` | mobygames logo | -| Motorola 6800 | `motorola-6800` | mobygames logo | -| Motorola 68k | `motorola-68k` | mobygames logo | -| MRE | `mre` | mobygames logo | -| MSX | `msx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| MSX Turbo R | `msx-turbo` | screenscraper logo | -| MSX2 | `msx2` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | -| MUGEN | `mugen` | launchbox logo | -| N-Gage | `ngage` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| N-Gage (service) | `ngage2` | mobygames logo | -| Namco System 22 | `system-32` | screenscraper logo launchbox logo | -| Nascom | `nascom` | mobygames logo | -| NEC PC-6000 Series | `nec-pc-6000-series` | igdb logo hasheous logo | -| Neo Geo AES | `neogeoaes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Neo Geo CD | `neo-geo-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Neo Geo MVS | `neogeomvs` | igdb logo screenscraper logo mobygames logo launchbox logo libretro logo | -| Neo Geo Pocket | `neo-geo-pocket` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Neo Geo Pocket Color | `neo-geo-pocket-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | -| Neo Geo X | `neo-geo-x` | mobygames logo | -| New Nintendo 3DS | `new-nintendo-3ds` | igdb logo mobygames logo hasheous logo | -| NewBrain | `newbrain` | mobygames logo | -| Newton | `newton` | mobygames logo | -| Nintendo 3DS | `3ds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Nintendo 64 | `n64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Nintendo 64DD | `64dd` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | -| Nintendo DS | `nds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Nintendo DSi | `nintendo-dsi` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | -| Nintendo Entertainment System | `nes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Nintendo GameCube | `ngc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Nintendo Switch | `switch` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Nintendo Switch 2 | `switch-2` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| North Star | `northstar` | mobygames logo | -| Noval 760 | `noval-760` | mobygames logo | -| Nuon | `nuon` | igdb logo mobygames logo launchbox logo | -| Oculus Go | `oculus-go` | igdb logo mobygames logo howlongtobeat logo | -| Oculus Quest | `oculus-quest` | igdb logo mobygames logo howlongtobeat logo | -| Oculus Rift | `oculus-rift` | igdb logo | -| Oculus VR | `oculus-vr` | igdb logo | -| Odyssey 2 / Videopac G7000 | `odyssey-2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Ohio Scientific | `ohio-scientific` | mobygames logo | -| OnLive Game System | `onlive-game-system` | igdb logo mobygames logo howlongtobeat logo | -| OOParts | `ooparts` | igdb logo mobygames logo | -| OpenBOR | `openbor` | screenscraper logo launchbox logo | -| Orao | `orao` | mobygames logo | -| Oric | `oric` | screenscraper logo mobygames logo | -| Oric Atmos | `atmos` | screenscraper logo launchbox logo | -| OS/2 | `os2` | mobygames logo | -| Othello Multivision | `multivision` | launchbox logo hasheous logo | -| Ouya | `ouya` | igdb logo mobygames logo launchbox logo howlongtobeat logo | -| Palm OS | `palm-os` | igdb logo screenscraper logo mobygames logo | -| Palmtex | `palmtex` | | -| Panasonic Jungle | `panasonic-jungle` | igdb logo | -| Panasonic M2 | `panasonic-m2` | igdb logo | -| Pandora | `pandora` | mobygames logo | -| PC Booter | `pc-booter` | mobygames logo | -| PC Engine SuperGrafx | `supergrafx` | igdb logo screenscraper logo mobygames logo launchbox logo libretro logo | -| PC-50X Family | `pc-50x-family` | igdb logo | -| PC-6001 | `pc-6001` | mobygames logo | -| PC-8000 | `pc-8000` | mobygames logo | -| PC-8800 Series | `pc-8800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| PC-9800 Series | `pc-9800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| PC-FX | `pc-fx` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo howlongtobeat logo libretro logo | -| PDP-1 | `pdp1` | igdb logo | -| PDP-10 | `pdp10` | igdb logo | -| PDP-11 | `pdp11` | igdb logo | -| PDP-7 | `pdp-7` | igdb logo | -| PDP-8 | `pdp-8` | igdb logo | -| Pebble | `pebble` | mobygames logo | -| Philips CD-i | `philips-cd-i` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Philips VG 5000 | `philips-vg-5000` | screenscraper logo mobygames logo launchbox logo | -| Photo CD | `photocd` | mobygames logo | -| PICO | `pico` | screenscraper logo mobygames logo launchbox logo howlongtobeat logo | -| Pinball | `pinball` | screenscraper logo launchbox logo | -| Pippin | `pippin` | mobygames logo | -| PLATO | `plato` | igdb logo | -| Playdate | `playdate` | igdb logo mobygames logo howlongtobeat logo | -| Playdia | `playdia` | igdb logo mobygames logo | -| PlayStation | `psx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| PlayStation 2 | `ps2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| PlayStation 3 | `ps3` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation 4 | `ps4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation 5 | `ps5` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation Now | `playstation-now` | mobygames logo howlongtobeat logo | -| PlayStation Portable | `psp` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| PlayStation Vita | `psvita` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| PlayStation VR | `psvr` | igdb logo howlongtobeat logo | -| PlayStation VR2 | `psvr2` | igdb logo | -| Plex Arcade | `plex-arcade` | mobygames logo | -| Plug & Play | `plug-and-play` | igdb logo howlongtobeat logo | -| PocketStation | `pocketstation` | igdb logo launchbox logo hasheous logo | -| Pokitto | `pokitto` | mobygames logo | -| Pokémon mini | `pokemon-mini` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | -| Poly-88 | `poly-88` | mobygames logo | -| Polymega | `polymega` | igdb logo | -| PSP Minis | `psp-minis` | screenscraper logo launchbox logo | -| R-Zone | `r-zone` | igdb logo | -| RCA Studio II | `rca-studio-ii` | mobygames logo launchbox logo hasheous logo | -| Research Machines 380Z | `research-machines-380z` | mobygames logo | -| Roku | `roku` | mobygames logo | -| SAM Coupé | `sam-coupe` | screenscraper logo mobygames logo launchbox logo | -| Satellaview | `satellaview` | igdb logo screenscraper logo launchbox logo libretro logo | -| SC/MP | `scmp` | mobygames logo | -| ScummVM | `scummvm` | igdb logo screenscraper logo launchbox logo libretro logo | -| SD-200/270/290 | `sd-200270290` | mobygames logo | -| SDS Sigma 7 | `sdssigma7` | igdb logo | -| Sega 32X | `sega32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Sega Advanced Pico Beena | `beena` | hasheous logo | -| Sega CD | `segacd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Sega CD 32X | `segacd32` | igdb logo launchbox logo | -| Sega Dreamcast VMU | `vmu` | launchbox logo | -| Sega Game Gear | `gamegear` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Sega Hikaru | `hikaru` | screenscraper logo launchbox logo | -| Sega Master System/Mark III | `sms` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Sega Mega Drive/Genesis | `genesis` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Sega Model 1 | `model1` | launchbox logo | -| Sega Model 2 | `model2` | screenscraper logo launchbox logo | -| Sega Model 3 | `model3` | screenscraper logo launchbox logo | -| Sega Pico | `sega-pico` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Sega Saturn | `saturn` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Sega SC-3000 | `sc3000` | launchbox logo hasheous logo | -| Sega ST-V | `stv` | screenscraper logo launchbox logo | -| Sega System 16 | `system16` | launchbox logo | -| Sega System 32 | `system32` | launchbox logo | -| SG-1000 | `sg1000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Sharp MZ-2200 | `sharp-mz-2200` | igdb logo | -| Sharp MZ-80B/2000/2500 | `sharp-mz-80b20002500` | mobygames logo launchbox logo | -| Sharp MZ-80K/700/800/1500 | `sharp-mz-80k7008001500` | mobygames logo | -| Sharp X1 | `x1` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Sharp X68000 | `sharp-x68000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Sharp Zaurus | `sharp-zaurus` | mobygames logo | -| Signetics 2650 | `signetics-2650` | mobygames logo | -| Sinclair QL | `sinclair-ql` | igdb logo mobygames logo hasheous logo | -| Sinclair ZX81 | `zx81` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| SK-VM | `sk-vm` | mobygames logo | -| SMC-777 | `smc-777` | mobygames logo | -| Socrates | `socrates` | mobygames logo launchbox logo | -| Sol-20 | `sol-20` | igdb logo mobygames logo | -| Sord M5 | `sord-m5` | mobygames logo launchbox logo | -| Spectravideo | `spectravideo` | screenscraper logo mobygames logo launchbox logo | -| SRI-500/1000 | `sri-5001000` | mobygames logo | -| SteamVR | `steam-vr` | igdb logo | -| Sufami Turbo | `sufami-turbo` | screenscraper logo libretro logo | -| Super A'Can | `super-acan` | igdb logo screenscraper logo mobygames logo | -| Super Famicom | `sfam` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | -| Super NES CD-ROM System | `super-nes-cd-rom-system` | igdb logo | -| Super Nintendo Entertainment System | `snes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Super Vision 8000 | `super-vision-8000` | mobygames logo launchbox logo hasheous logo | -| Sure Shot HD | `sure-shot-hd` | mobygames logo | -| SwanCrystal | `swancrystal` | igdb logo | -| SWTPC 6800 | `swtpc-6800` | mobygames logo | -| Symbian | `symbian` | mobygames logo | -| TADS | `tads` | mobygames logo | -| Taito X-55 | `taito-x-55` | screenscraper logo mobygames logo | -| Tandy Vis | `tandy-vis` | | -| Tapwave Zodiac | `zod` | igdb logo launchbox logo | -| Tatung Einstein | `tatung-einstein` | igdb logo mobygames logo | -| Tektronix 4050 | `tektronix-4050` | mobygames logo | -| Tele-Spiel ES-2201 | `tele-spiel` | mobygames logo | -| Telstar Arcade | `telstar-arcade` | mobygames logo | -| Terebikko / See 'n Say Video Phone | `terebikko-slash-see-n-say-video-phone` | igdb logo | -| Terminal | `terminal` | mobygames logo | -| Texas Instruments TI-82 | `ti-82` | hasheous logo | -| Texas Instruments TI-83 | `ti-83` | hasheous logo | -| Texas Instruments TI-99 | `ti-99` | igdb logo screenscraper logo mobygames logo | -| Thomson MO5 | `thomson-mo5` | igdb logo screenscraper logo mobygames logo | -| Thomson TO | `thomson-to` | screenscraper logo mobygames logo | -| TI Programmable Calculator | `ti-programmable-calculator` | mobygames logo | -| TI-99/4A | `ti-994a` | screenscraper logo mobygames logo launchbox logo | -| TIC-80 | `tic-80` | screenscraper logo libretro logo | -| Tiki 100 | `tiki-100` | mobygames logo | -| TIM | `tim` | mobygames logo | -| Timex Sinclair 2068 | `timex-sinclair-2068` | mobygames logo | -| Tizen | `tizen` | mobygames logo | -| Tomahawk F1 | `tomahawk-f1` | mobygames logo | -| Tomy Tutor | `tomy-tutor` | mobygames logo launchbox logo libretro logo | -| Tomy Tutor / Pyuta / Grandstand Tutor | `tomy-tutor-slash-pyuta-slash-grandstand-tutor` | igdb logo | -| Triton | `triton` | mobygames logo | -| TRS-80 | `trs-80` | igdb logo mobygames logo launchbox logo hasheous logo | -| TRS-80 Color Computer | `trs-80-color-computer` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | -| TRS-80 MC-10 | `trs-80-mc-10` | mobygames logo | -| TRS-80 Model 100 | `trs-80-model-100` | mobygames logo | -| TurboGrafx-16/PC Engine | `tg16` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Turbografx-16/PC Engine CD | `turbografx-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| tvOS | `tvos` | mobygames logo | -| Type X | `type-x` | screenscraper logo launchbox logo | -| Uzebox | `uzebox` | igdb logo screenscraper logo retroachivements logo | -| V.Flash | `vflash` | mobygames logo | -| V.Smile | `vsmile` | igdb logo screenscraper logo mobygames logo launchbox logo | -| VC 4000 | `vc-4000` | igdb logo screenscraper logo launchbox logo | -| Vector-06C | `06c` | launchbox logo | -| Vectrex | `vectrex` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Versatile | `versatile` | mobygames logo | -| VideoBrain | `videobrain` | mobygames logo | -| Videopac+ G7400 | `videopac-g7400` | screenscraper logo mobygames logo launchbox logo | -| Virtual Boy | `virtualboy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Virtual Console | `vc` | igdb logo | -| VIS | `vis` | mobygames logo | -| visionOS | `visionos` | igdb logo | -| Visual Memory Unit / Visual Memory System | `visual-memory-unit-slash-visual-memory-system` | igdb logo | -| Wang 2200 | `wang2200` | mobygames logo | -| WASM-4 | `wasm-4` | screenscraper logo retroachivements logo | -| Watara/QuickShot Supervision | `supervision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | -| watchOS | `watchos` | mobygames logo | -| webOS | `webos` | mobygames logo | -| Wii | `wii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| Wii U | `wiiu` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Windows | `win` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | -| Windows 3.x | `win3x` | screenscraper logo mobygames logo launchbox logo | -| Windows Apps | `windows-apps` | mobygames logo | -| Windows Mixed Reality | `windows-mixed-reality` | igdb logo | -| Windows Mobile | `windows-mobile` | igdb logo mobygames logo | -| Windows Phone | `winphone` | igdb logo mobygames logo howlongtobeat logo | -| WIPI | `wipi` | mobygames logo | -| WonderSwan | `wonderswan` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | -| WonderSwan Color | `wonderswan-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | -| XaviXPORT | `xavixport` | mobygames logo launchbox logo | -| Xbox | `xbox` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| Xbox 360 | `xbox360` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Xbox Cloud Gaming | `xboxcloudgaming` | mobygames logo | -| Xbox One | `xboxone` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Xbox Series X/S | `series-x-s` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | -| Xerox Alto | `xerox-alto` | mobygames logo | -| Z-machine | `z-machine` | screenscraper logo mobygames logo | -| Zeebo | `zeebo` | igdb logo mobygames logo howlongtobeat logo | -| Zilog Z80 | `z80` | mobygames logo | -| Zilog Z8000 | `zilog-z8000` | mobygames logo | -| ZiNc | `zinc` | launchbox logo | -| Zodiac | `zodiac` | mobygames logo | -| Zune | `zune` | mobygames logo | -| ZX Spectrum | `zxs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | -| ZX Spectrum Next | `zx-spectrum-next` | mobygames logo | -| ZX80 | `zx80` | mobygames logo hasheous logo | +| Platform Name | Folder Name | Metadata Providers | +| ----------------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1292 Advanced Programmable Video System | `1292-advanced-programmable-video-system` | igdb logo mobygames logo | +| 3DO Interactive Multiplayer | `3do` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| 8-Bit Productions Commander X16 | `commander-x16` | hasheous logo | +| Aamber Pegasus | `pegasus` | screenscraper logo launchbox logo | +| ABC 80 | `abc-80` | mobygames logo | +| Acorn Archimedes | `acorn-archimedes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Acorn Electron | `acorn-electron` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Action Max | `action-max` | screenscraper logo launchbox logo hasheous logo | +| Advanced Pico Beena | `advanced-pico-beena` | igdb logo | +| Adventure Vision | `adventure-vision` | screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | +| AirConsole | `airconsole` | igdb logo mobygames logo | +| Alice 32/90 | `alice-3290` | mobygames logo launchbox logo | +| Altair 680 | `altair-680` | mobygames logo | +| Altair 8800 | `altair-8800` | mobygames logo hasheous logo | +| Amazon Alexa | `amazon-alexa` | mobygames logo | +| Amazon Fire TV | `amazon-fire-tv` | igdb logo mobygames logo | +| Amiga | `amiga` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Amiga CD | `amiga-cd` | screenscraper logo | +| Amiga CD32 | `amiga-cd32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Amstrad CPC | `acpc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Amstrad GX4000 | `amstrad-gx4000` | igdb logo screenscraper logo launchbox logo hasheous logo | +| Amstrad PCW | `amstrad-pcw` | igdb logo mobygames logo hasheous logo | +| Analogue electronics | `analogueelectronics` | igdb logo | +| Android | `android` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Antstream | `antstream` | mobygames logo | +| APF MP1000/Imagination Machine | `apf` | mobygames logo launchbox logo hasheous logo | +| Apogee BK-01 | `bk-01` | launchbox logo | +| Apple I | `apple` | screenscraper logo mobygames logo hasheous logo | +| Apple II | `appleii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| Apple IIGS | `apple-iigs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Apple III | `appleiii` | screenscraper logo hasheous logo | +| Apple Lisa | `apple-lisa` | hasheous logo | +| Apple Pippin | `apple-pippin` | igdb logo hasheous logo | +| Arcade | `arcade` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| Arcadia 2001 | `arcadia-2001` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo | +| Arduboy | `arduboy` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | +| Astral 2000 | `astral-2000` | mobygames logo | +| Atari 2600 | `atari2600` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari 5200 | `atari5200` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Atari 7800 | `atari7800` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari 8-bit | `atari8bit` | igdb logo screenscraper logo mobygames logo hasheous logo howlongtobeat logo | +| Atari 800 | `atari800` | screenscraper logo launchbox logo | +| Atari Jaguar | `jaguar` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari Jaguar CD | `atari-jaguar-cd` | igdb logo screenscraper logo launchbox logo retroachivements logo howlongtobeat logo | +| Atari Lynx | `lynx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Atari ST/STE | `atari-st` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Atari VCS | `atari-vcs` | mobygames logo | +| Atari XEGS | `atari-xegs` | screenscraper logo launchbox logo | +| Atom | `atom` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| AY-3-8500 | `ay-3-8500` | igdb logo | +| AY-3-8603 | `ay-3-8603` | igdb logo | +| AY-3-8605 | `ay-3-8605` | igdb logo | +| AY-3-8606 | `ay-3-8606` | igdb logo | +| AY-3-8607 | `ay-3-8607` | igdb logo | +| AY-3-8610 | `ay-3-8610` | igdb logo | +| AY-3-8710 | `ay-3-8710` | igdb logo | +| AY-3-8760 | `ay-3-8760` | igdb logo | +| Bada | `bada` | mobygames logo | +| Bally Astrocade | `astrocade` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| BBC Microcomputer System | `bbcmicro` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Benesse Pocket Challenge V2 | `pocket-challenge-v2` | screenscraper logo hasheous logo | +| Benesse Pocket Challenge W | `pocket-challenge-w` | hasheous logo | +| BeOS | `beos` | mobygames logo | +| BGR Computers Excalibur 64 | `excalibur-64` | hasheous logo | +| Bit Corporation BIT 90 | `bit-90` | hasheous logo | +| Black Point | `black-point` | | +| BlackBerry OS | `blackberry` | igdb logo mobygames logo | +| Blacknut | `blacknut` | mobygames logo | +| Blu-ray Player | `blu-ray-player` | igdb logo mobygames logo | +| BREW | `brew` | mobygames logo | +| Browser (Flash/HTML5) | `browser` | igdb logo mobygames logo launchbox logo flashpoint logo howlongtobeat logo | +| Bubble | `bubble` | mobygames logo | +| Call-A-Computer time-shared mainframe computer system | `call-a-computer` | igdb logo | +| Cambridge Computer Z88 | `z88` | hasheous logo | +| Camputers Lynx | `camputers-lynx` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| Capcom Play System | `cps1` | screenscraper logo | +| Capcom Play System 2 | `cps2` | screenscraper logo | +| Capcom Play System 3 | `cps3` | screenscraper logo | +| Casio CFX-9850 | `casio-cfx-9850` | hasheous logo | +| Casio FP-1000 & FP-1100 | `casio-fp-1000` | hasheous logo | +| Casio Loopy | `casio-loopy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Casio PB-1000 | `casio-pb-1000` | hasheous logo | +| Casio Programmable Calculator | `casio-programmable-calculator` | mobygames logo | +| Casio PV-1000 | `casio-pv-1000` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| Casio PV-2000 | `casio-pv-2000` | hasheous logo | +| CDC Cyber 70 | `cdccyber70` | igdb logo | +| Champion 2711 | `champion-2711` | mobygames logo | +| ClickStart | `clickstart` | mobygames logo | +| Coleco Adam | `colecoadam` | screenscraper logo mobygames logo launchbox logo | +| ColecoVision | `colecovision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Colour Genie | `colour-genie` | screenscraper logo mobygames logo launchbox logo | +| Commodore 128 | `c128` | screenscraper logo mobygames logo launchbox logo hasheous logo | +| Commodore 16 | `c16` | igdb logo screenscraper logo mobygames logo hasheous logo | +| Commodore C64/128/MAX | `c64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Commodore CDTV | `commodore-cdtv` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| Commodore PET | `cpet` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Commodore Plus/4 | `c-plus-4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Commodore VIC-20 | `vic-20` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | +| Compal 80 | `compal-80` | mobygames logo | +| Compucolor I | `compucolor-i` | mobygames logo | +| Compucolor II | `compucolor-ii` | mobygames logo | +| Compucorp Programmable Calculator | `compucorp-programmable-calculator` | mobygames logo | +| COSMAC | `fred-cosmac` | mobygames logo | +| CP/M | `cpm` | mobygames logo | +| CreatiVision | `creativision` | screenscraper logo mobygames logo launchbox logo | +| Cybervision | `cybervision` | mobygames logo | +| Danger OS | `danger-os` | mobygames logo | +| Daydream | `daydream` | igdb logo | +| DEC GT40 | `gt40` | igdb logo | +| Dedicated console | `dedicated-console` | mobygames logo | +| Dedicated handheld | `dedicated-handheld` | mobygames logo | +| Didj | `didj` | mobygames logo | +| Digiblast | `digiblast` | igdb logo mobygames logo | +| DoJa | `doja` | mobygames logo | +| Donner Model 30 | `donner30` | igdb logo | +| DOS | `dos` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo libretro logo | +| Dragon 32/64 | `dragon-32-slash-64` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Dreamcast | `dc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| DVD Player | `dvd-player` | igdb logo mobygames logo howlongtobeat logo | +| e-Reader / Card-e Reader | `e-reader-slash-card-e-reader` | igdb logo | +| ECD Micromind | `ecd-micromind` | mobygames logo | +| EDSAC | `edsac` | igdb logo | +| Elektor TV Games Computer | `elektor` | igdb logo retroachivements logo | +| Elektronika BK | `bk` | screenscraper logo launchbox logo | +| Enterprise | `enterprise` | mobygames logo launchbox logo | +| Epoch Cassette Vision | `epoch-cassette-vision` | igdb logo mobygames logo | +| Epoch Game Pocket Computer | `epoch-game-pocket-computer` | screenscraper logo mobygames logo launchbox logo | +| Epoch Super Cassette Vision | `epoch-super-cassette-vision` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Evercade | `evercade` | igdb logo mobygames logo howlongtobeat logo | +| Exelvision | `exelvision` | screenscraper logo mobygames logo launchbox logo | +| ExEn | `exen` | mobygames logo | +| Exidy Sorcerer | `exidy-sorcerer` | igdb logo screenscraper logo mobygames logo launchbox logo | +| Fairchild Channel F | `fairchild-channel-f` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Family Computer | `famicom` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo libretro logo | +| Family Computer Disk System | `fds` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | +| Feature phone | `mobile-custom` | mobygames logo | +| Ferranti Nimrod Computer | `nimrod` | igdb logo | +| FM Towns | `fm-towns` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| FM-7 | `fm-7` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| Freebox | `freebox` | mobygames logo | +| G-cluster | `g-cluster` | mobygames logo | +| Galaksija | `galaksija` | mobygames logo | +| Gamate | `gamate` | igdb logo screenscraper logo hasheous logo | +| Game & Watch | `g-and-w` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| Game Boy | `gb` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Game Boy Advance | `gba` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Game Boy Color | `gbc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Game Master | `hartung` | screenscraper logo launchbox logo | +| Game Wave | `game-wave` | mobygames logo launchbox logo | +| Game.com | `game-dot-com` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| GameStick | `gamestick` | mobygames logo | +| Gear VR | `gear-vr` | igdb logo howlongtobeat logo | +| GIMINI | `gimini` | mobygames logo | +| Gizmondo | `gizmondo` | igdb logo mobygames logo howlongtobeat logo | +| Gloud | `gloud` | mobygames logo | +| Glulx | `glulx` | mobygames logo | +| GNEX | `gnex` | mobygames logo | +| Google Stadia | `stadia` | igdb logo mobygames logo howlongtobeat logo | +| GP2X | `gp2x` | mobygames logo | +| GP2X Wiz | `gp2x-wiz` | mobygames logo | +| GP32 | `gp32` | screenscraper logo mobygames logo launchbox logo | +| GVM | `gvm` | mobygames logo | +| Handheld Electronic LCD | `handheld-electronic-lcd` | igdb logo | +| HD DVD Player | `hd-dvd-player` | mobygames logo | +| Heath/Zenith H8/H89 | `heathzenith` | mobygames logo | +| Heathkit H11 | `heathkit-h11` | mobygames logo | +| Hector HRX | `hrx` | launchbox logo | +| Hitachi S1 | `hitachi-s1` | mobygames logo | +| HP 2100 | `hp2100` | igdb logo | +| HP 3000 | `hp3000` | igdb logo | +| HP 9800 | `hp-9800` | mobygames logo | +| HP Programmable Calculator | `hp-programmable-calculator` | mobygames logo | +| Hugo | `hugo` | mobygames logo | +| Hyper Neo Geo 64 | `hyper-neo-geo-64` | igdb logo | +| HyperScan | `hyperscan` | igdb logo mobygames logo launchbox logo | +| IBM 5100 | `ibm-5100` | mobygames logo | +| IBM PCjr | `pc-jr` | hasheous logo | +| Ideal-Computer | `ideal-computer` | mobygames logo | +| iiRcade | `iircade` | mobygames logo | +| Imlac PDS-1 | `imlac-pds1` | igdb logo | +| Intel 8008 | `intel-8008` | mobygames logo | +| Intel 8080 | `intel-8080` | mobygames logo | +| Intel 8086 / 8088 | `intel-8086` | mobygames logo | +| Intellivision | `intellivision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Intellivision Amico | `intellivision-amico` | igdb logo | +| Interact Model One | `interact-model-one` | mobygames logo | +| Interton VC 4000 | `interton-vc-4000` | retroachivements logo | +| Interton Video 2000 | `interton-video-2000` | mobygames logo | +| iOS | `ios` | igdb logo mobygames logo launchbox logo | +| iPad | `ipad` | mobygames logo | +| iPod Classic | `ipod-classic` | mobygames logo | +| J2ME | `j2me` | mobygames logo | +| Jolt | `jolt` | mobygames logo | +| Jupiter Ace | `jupiter-ace` | screenscraper logo mobygames logo launchbox logo | +| KaiOS | `kaios` | mobygames logo | +| KIM-1 | `kim-1` | mobygames logo | +| Kindle Classic | `kindle` | mobygames logo | +| Laser 200 | `laser200` | mobygames logo | +| LaserActive | `laseractive` | igdb logo mobygames logo | +| LeapFrog Explorer | `leapfrog-explorer` | mobygames logo | +| Leapster | `leapster` | igdb logo mobygames logo | +| Leapster Explorer/LeadPad Explorer | `leapster-explorer-slash-leadpad-explorer` | igdb logo mobygames logo | +| LeapTV | `leaptv` | igdb logo mobygames logo | +| Legacy Computer | `legacy-computer` | igdb logo | +| Legacy Mobile Device | `mobile` | igdb logo howlongtobeat logo | +| Linux | `linux` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Luna | `luna` | mobygames logo howlongtobeat logo | +| Mac | `mac` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Maemo | `maemo` | mobygames logo | +| Magnavox Odyssey | `odyssey` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Mainframe | `mainframe` | mobygames logo | +| Matsushita/Panasonic JR | `matsushitapanasonic-jr` | mobygames logo | +| Mattel Aquarius | `aquarius` | mobygames logo launchbox logo hasheous logo | +| MeeGo | `meego` | mobygames logo | +| Mega Duck/Cougar Boy | `mega-duck-slash-cougar-boy` | igdb logo launchbox logo retroachivements logo | +| Memotech MTX | `memotech-mtx` | mobygames logo | +| Memotech MTX512 | `mtx512` | launchbox logo | +| Meritum | `meritum` | mobygames logo | +| Meta Quest 2 | `meta-quest-2` | igdb logo | +| Meta Quest 3 | `meta-quest-3` | igdb logo | +| Microbee | `microbee` | mobygames logo hasheous logo | +| Microcomputer | `microcomputer` | igdb logo | +| Microsoft MSX2+ | `msx2plus` | screenscraper logo launchbox logo | +| Microtan 65 | `microtan-65` | mobygames logo | +| Microvision | `microvision` | igdb logo mobygames logo | +| Mophun | `mophun` | mobygames logo | +| MOS Technology 6502 | `mos-technology-6502` | mobygames logo | +| Motorola 6800 | `motorola-6800` | mobygames logo | +| Motorola 68k | `motorola-68k` | mobygames logo | +| MRE | `mre` | mobygames logo | +| MSX | `msx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| MSX Turbo R | `msx-turbo` | screenscraper logo | +| MSX2 | `msx2` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | +| MUGEN | `mugen` | launchbox logo | +| N-Gage | `ngage` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| N-Gage (service) | `ngage2` | mobygames logo | +| Namco System 22 | `system-32` | screenscraper logo launchbox logo | +| Nascom | `nascom` | mobygames logo | +| NEC PC-6000 Series | `nec-pc-6000-series` | igdb logo hasheous logo | +| Neo Geo AES | `neogeoaes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Neo Geo CD | `neo-geo-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Neo Geo MVS | `neogeomvs` | igdb logo screenscraper logo mobygames logo launchbox logo libretro logo | +| Neo Geo Pocket | `neo-geo-pocket` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Neo Geo Pocket Color | `neo-geo-pocket-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Neo Geo X | `neo-geo-x` | mobygames logo | +| New Nintendo 3DS | `new-nintendo-3ds` | igdb logo mobygames logo hasheous logo | +| NewBrain | `newbrain` | mobygames logo | +| Newton | `newton` | mobygames logo | +| Nintendo 3DS | `3ds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Nintendo 64 | `n64` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo 64DD | `64dd` | igdb logo screenscraper logo launchbox logo hasheous logo libretro logo | +| Nintendo DS | `nds` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo DSi | `nintendo-dsi` | igdb logo screenscraper logo mobygames logo hasheous logo retroachivements logo | +| Nintendo Entertainment System | `nes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo GameCube | `ngc` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Nintendo Switch | `switch` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Nintendo Switch 2 | `switch-2` | igdb logo screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| North Star | `northstar` | mobygames logo | +| Noval 760 | `noval-760` | mobygames logo | +| Nuon | `nuon` | igdb logo mobygames logo launchbox logo | +| Oculus Go | `oculus-go` | igdb logo mobygames logo howlongtobeat logo | +| Oculus Quest | `oculus-quest` | igdb logo mobygames logo howlongtobeat logo | +| Oculus Rift | `oculus-rift` | igdb logo | +| Oculus VR | `oculus-vr` | igdb logo | +| Odyssey 2 / Videopac G7000 | `odyssey-2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Ohio Scientific | `ohio-scientific` | mobygames logo | +| OnLive Game System | `onlive-game-system` | igdb logo mobygames logo howlongtobeat logo | +| OOParts | `ooparts` | igdb logo mobygames logo | +| OpenBOR | `openbor` | screenscraper logo launchbox logo | +| Orao | `orao` | mobygames logo | +| Oric | `oric` | screenscraper logo mobygames logo | +| Oric Atmos | `atmos` | screenscraper logo launchbox logo | +| OS/2 | `os2` | mobygames logo | +| Othello Multivision | `multivision` | launchbox logo hasheous logo | +| Ouya | `ouya` | igdb logo mobygames logo launchbox logo howlongtobeat logo | +| Palm OS | `palm-os` | igdb logo screenscraper logo mobygames logo | +| Palmtex | `palmtex` | | +| Panasonic Jungle | `panasonic-jungle` | igdb logo | +| Panasonic M2 | `panasonic-m2` | igdb logo | +| Pandora | `pandora` | mobygames logo | +| PC Booter | `pc-booter` | mobygames logo | +| PC Engine SuperGrafx | `supergrafx` | igdb logo screenscraper logo mobygames logo launchbox logo libretro logo | +| PC-50X Family | `pc-50x-family` | igdb logo | +| PC-6001 | `pc-6001` | mobygames logo | +| PC-8000 | `pc-8000` | mobygames logo | +| PC-8800 Series | `pc-8800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PC-9800 Series | `pc-9800-series` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| PC-FX | `pc-fx` | igdb logo screenscraper logo mobygames logo launchbox logo retroachivements logo howlongtobeat logo libretro logo | +| PDP-1 | `pdp1` | igdb logo | +| PDP-10 | `pdp10` | igdb logo | +| PDP-11 | `pdp11` | igdb logo | +| PDP-7 | `pdp-7` | igdb logo | +| PDP-8 | `pdp-8` | igdb logo | +| Pebble | `pebble` | mobygames logo | +| Philips CD-i | `philips-cd-i` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Philips VG 5000 | `philips-vg-5000` | screenscraper logo mobygames logo launchbox logo | +| Photo CD | `photocd` | mobygames logo | +| PICO | `pico` | screenscraper logo mobygames logo launchbox logo howlongtobeat logo | +| Pinball | `pinball` | screenscraper logo launchbox logo | +| Pippin | `pippin` | mobygames logo | +| PLATO | `plato` | igdb logo | +| Playdate | `playdate` | igdb logo mobygames logo howlongtobeat logo | +| Playdia | `playdia` | igdb logo mobygames logo | +| PlayStation | `psx` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PlayStation 2 | `ps2` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PlayStation 3 | `ps3` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation 4 | `ps4` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation 5 | `ps5` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation Now | `playstation-now` | mobygames logo howlongtobeat logo | +| PlayStation Portable | `psp` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| PlayStation Vita | `psvita` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| PlayStation VR | `psvr` | igdb logo howlongtobeat logo | +| PlayStation VR2 | `psvr2` | igdb logo | +| Plex Arcade | `plex-arcade` | mobygames logo | +| Plug & Play | `plug-and-play` | igdb logo howlongtobeat logo | +| PocketStation | `pocketstation` | igdb logo launchbox logo hasheous logo | +| Pokitto | `pokitto` | mobygames logo | +| Pokémon mini | `pokemon-mini` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Poly-88 | `poly-88` | mobygames logo | +| Polymega | `polymega` | igdb logo | +| PSP Minis | `psp-minis` | screenscraper logo launchbox logo | +| R-Zone | `r-zone` | igdb logo | +| RCA Studio II | `rca-studio-ii` | mobygames logo launchbox logo hasheous logo | +| Research Machines 380Z | `research-machines-380z` | mobygames logo | +| Roku | `roku` | mobygames logo | +| SAM Coupé | `sam-coupe` | screenscraper logo mobygames logo launchbox logo | +| Satellaview | `satellaview` | igdb logo screenscraper logo launchbox logo libretro logo | +| SC/MP | `scmp` | mobygames logo | +| ScummVM | `scummvm` | igdb logo screenscraper logo launchbox logo libretro logo | +| SD-200/270/290 | `sd-200270290` | mobygames logo | +| SDS Sigma 7 | `sdssigma7` | igdb logo | +| Sega 32X | `sega32` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Advanced Pico Beena | `beena` | hasheous logo | +| Sega CD | `segacd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega CD 32X | `segacd32` | igdb logo launchbox logo | +| Sega Dreamcast VMU | `vmu` | launchbox logo | +| Sega Game Gear | `gamegear` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Hikaru | `hikaru` | screenscraper logo launchbox logo | +| Sega Master System/Mark III | `sms` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Mega Drive/Genesis | `genesis` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega Model 1 | `model1` | launchbox logo | +| Sega Model 2 | `model2` | screenscraper logo launchbox logo | +| Sega Model 3 | `model3` | screenscraper logo launchbox logo | +| Sega Pico | `sega-pico` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Sega Saturn | `saturn` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sega SC-3000 | `sc3000` | launchbox logo hasheous logo | +| Sega ST-V | `stv` | screenscraper logo launchbox logo | +| Sega System 16 | `system16` | launchbox logo | +| Sega System 32 | `system32` | launchbox logo | +| SG-1000 | `sg1000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Sharp MZ-2200 | `sharp-mz-2200` | igdb logo | +| Sharp MZ-80B/2000/2500 | `sharp-mz-80b20002500` | mobygames logo launchbox logo | +| Sharp MZ-80K/700/800/1500 | `sharp-mz-80k7008001500` | mobygames logo | +| Sharp X1 | `x1` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Sharp X68000 | `sharp-x68000` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Sharp Zaurus | `sharp-zaurus` | mobygames logo | +| Signetics 2650 | `signetics-2650` | mobygames logo | +| Sinclair QL | `sinclair-ql` | igdb logo mobygames logo hasheous logo | +| Sinclair ZX81 | `zx81` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| SK-VM | `sk-vm` | mobygames logo | +| SMC-777 | `smc-777` | mobygames logo | +| Socrates | `socrates` | mobygames logo launchbox logo | +| Sol-20 | `sol-20` | igdb logo mobygames logo | +| Sord M5 | `sord-m5` | mobygames logo launchbox logo | +| Spectravideo | `spectravideo` | screenscraper logo mobygames logo launchbox logo | +| SRI-500/1000 | `sri-5001000` | mobygames logo | +| SteamVR | `steam-vr` | igdb logo | +| Sufami Turbo | `sufami-turbo` | screenscraper logo libretro logo | +| Super A'Can | `super-acan` | igdb logo screenscraper logo mobygames logo | +| Super Famicom | `sfam` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| Super NES CD-ROM System | `super-nes-cd-rom-system` | igdb logo | +| Super Nintendo Entertainment System | `snes` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Super Vision 8000 | `super-vision-8000` | mobygames logo launchbox logo hasheous logo | +| Sure Shot HD | `sure-shot-hd` | mobygames logo | +| SwanCrystal | `swancrystal` | igdb logo | +| SWTPC 6800 | `swtpc-6800` | mobygames logo | +| Symbian | `symbian` | mobygames logo | +| TADS | `tads` | mobygames logo | +| Taito X-55 | `taito-x-55` | screenscraper logo mobygames logo | +| Tandy Vis | `tandy-vis` | | +| Tapwave Zodiac | `zod` | igdb logo launchbox logo | +| Tatung Einstein | `tatung-einstein` | igdb logo mobygames logo | +| Tektronix 4050 | `tektronix-4050` | mobygames logo | +| Tele-Spiel ES-2201 | `tele-spiel` | mobygames logo | +| Telstar Arcade | `telstar-arcade` | mobygames logo | +| Terebikko / See 'n Say Video Phone | `terebikko-slash-see-n-say-video-phone` | igdb logo | +| Terminal | `terminal` | mobygames logo | +| Texas Instruments TI-82 | `ti-82` | hasheous logo | +| Texas Instruments TI-83 | `ti-83` | hasheous logo | +| Texas Instruments TI-99 | `ti-99` | igdb logo screenscraper logo mobygames logo | +| Thomson MO5 | `thomson-mo5` | igdb logo screenscraper logo mobygames logo | +| Thomson TO | `thomson-to` | screenscraper logo mobygames logo | +| TI Programmable Calculator | `ti-programmable-calculator` | mobygames logo | +| TI-99/4A | `ti-994a` | screenscraper logo mobygames logo launchbox logo | +| TIC-80 | `tic-80` | screenscraper logo libretro logo | +| Tiki 100 | `tiki-100` | mobygames logo | +| TIM | `tim` | mobygames logo | +| Timex Sinclair 2068 | `timex-sinclair-2068` | mobygames logo | +| Tizen | `tizen` | mobygames logo | +| Tomahawk F1 | `tomahawk-f1` | mobygames logo | +| Tomy Tutor | `tomy-tutor` | mobygames logo launchbox logo libretro logo | +| Tomy Tutor / Pyuta / Grandstand Tutor | `tomy-tutor-slash-pyuta-slash-grandstand-tutor` | igdb logo | +| Triton | `triton` | mobygames logo | +| TRS-80 | `trs-80` | igdb logo mobygames logo launchbox logo hasheous logo | +| TRS-80 Color Computer | `trs-80-color-computer` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo | +| TRS-80 MC-10 | `trs-80-mc-10` | mobygames logo | +| TRS-80 Model 100 | `trs-80-model-100` | mobygames logo | +| TurboGrafx-16/PC Engine | `tg16` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Turbografx-16/PC Engine CD | `turbografx-cd` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| tvOS | `tvos` | mobygames logo | +| Type X | `type-x` | screenscraper logo launchbox logo | +| Uzebox | `uzebox` | igdb logo screenscraper logo retroachivements logo | +| V.Flash | `vflash` | mobygames logo | +| V.Smile | `vsmile` | igdb logo screenscraper logo mobygames logo launchbox logo | +| VC 4000 | `vc-4000` | igdb logo screenscraper logo launchbox logo | +| Vector-06C | `06c` | launchbox logo | +| Vectrex | `vectrex` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Versatile | `versatile` | mobygames logo | +| VideoBrain | `videobrain` | mobygames logo | +| Videopac+ G7400 | `videopac-g7400` | screenscraper logo mobygames logo launchbox logo | +| Virtual Boy | `virtualboy` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Virtual Console | `vc` | igdb logo | +| VIS | `vis` | mobygames logo | +| visionOS | `visionos` | igdb logo | +| Visual Memory Unit / Visual Memory System | `visual-memory-unit-slash-visual-memory-system` | igdb logo | +| Wang 2200 | `wang2200` | mobygames logo | +| WASM-4 | `wasm-4` | screenscraper logo retroachivements logo | +| Watara/QuickShot Supervision | `supervision` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| watchOS | `watchos` | mobygames logo | +| webOS | `webos` | mobygames logo | +| Wii | `wii` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| Wii U | `wiiu` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Windows | `win` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo | +| Windows 3.x | `win3x` | screenscraper logo mobygames logo launchbox logo | +| Windows Apps | `windows-apps` | mobygames logo | +| Windows Mixed Reality | `windows-mixed-reality` | igdb logo | +| Windows Mobile | `windows-mobile` | igdb logo mobygames logo | +| Windows Phone | `winphone` | igdb logo mobygames logo howlongtobeat logo | +| WIPI | `wipi` | mobygames logo | +| WonderSwan | `wonderswan` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo howlongtobeat logo libretro logo | +| WonderSwan Color | `wonderswan-color` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo retroachivements logo libretro logo | +| XaviXPORT | `xavixport` | mobygames logo launchbox logo | +| Xbox | `xbox` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| Xbox 360 | `xbox360` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Xbox Cloud Gaming | `xboxcloudgaming` | mobygames logo | +| Xbox One | `xboxone` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Xbox Series X/S | `series-x-s` | igdb logo mobygames logo launchbox logo hasheous logo howlongtobeat logo | +| Xerox Alto | `xerox-alto` | mobygames logo | +| Z-machine | `z-machine` | screenscraper logo mobygames logo | +| Zeebo | `zeebo` | igdb logo mobygames logo howlongtobeat logo | +| Zilog Z80 | `z80` | mobygames logo | +| Zilog Z8000 | `zilog-z8000` | mobygames logo | +| ZiNc | `zinc` | launchbox logo | +| Zodiac | `zodiac` | mobygames logo | +| Zune | `zune` | mobygames logo | +| ZX Spectrum | `zxs` | igdb logo screenscraper logo mobygames logo launchbox logo hasheous logo howlongtobeat logo libretro logo | +| ZX Spectrum Next | `zx-spectrum-next` | mobygames logo | +| ZX80 | `zx80` | mobygames logo hasheous logo | diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index d83752ba..387b403e 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -99,7 +99,7 @@ Fix: add the property mapping documented in [OIDC with Authentik → Create a pr Two possibilities: 1. **Email not verified in Keycloak**: Admin Console → Users → open the user → **Email Verified**: on. RomM rejects unverified emails. -2. **Email mismatch between Keycloak and a pre-existing RomM user**: if RomM already has a local account `alice@example.com`, the first OIDC login for `alice@example.com` signs into that account. If the emails don't match exactly, RomM creates a *second* account. Fix: edit the user in RomM to set the correct email, then log in via OIDC. +2. **Email mismatch between Keycloak and a pre-existing RomM user**: if RomM already has a local account `alice@example.com`, the first OIDC login for `alice@example.com` signs into that account. If the emails don't match exactly, RomM creates a _second_ account. Fix: edit the user in RomM to set the correct email, then log in via OIDC. ### `OAuthException: expired token` on callback diff --git a/docs/troubleshooting/in-browser-play.md b/docs/troubleshooting/in-browser-play.md index 174f17ac..59c859c4 100644 --- a/docs/troubleshooting/in-browser-play.md +++ b/docs/troubleshooting/in-browser-play.md @@ -97,13 +97,13 @@ In-browser emulation is CPU-heavy. Mobile tips: ## Known limitations by core -| Core | Known issues | -| --- | --- | -| `ppsspp` (PSP) | Not supported in Console Mode. | -| `dosbox-pure` (DOS) | Not supported in Console Mode. Some DOS quirks. See `disable_batch_bootup`. | -| `mednafen_saturn` | Audio stutter on weak hardware. `yabause` is an alternative. | -| `opera` (3DO) | Niche. Limited ROM compatibility. | -| All Nintendo DS cores | Touchscreen emulation is imperfect on non-touch devices. | +| Core | Known issues | +| --------------------- | --------------------------------------------------------------------------- | +| `ppsspp` (PSP) | Not supported in Console Mode. | +| `dosbox-pure` (DOS) | Not supported in Console Mode. Some DOS quirks. See `disable_batch_bootup`. | +| `mednafen_saturn` | Audio stutter on weak hardware. `yabause` is an alternative. | +| `opera` (3DO) | Niche. Limited ROM compatibility. | +| All Nintendo DS cores | Touchscreen emulation is imperfect on non-touch devices. | ## Still stuck diff --git a/docs/troubleshooting/kubernetes.md b/docs/troubleshooting/kubernetes.md index c4b13cb9..e9170f02 100644 --- a/docs/troubleshooting/kubernetes.md +++ b/docs/troubleshooting/kubernetes.md @@ -15,12 +15,12 @@ Fix: disable service-link env vars on the RomM pod. apiVersion: apps/v1 kind: Deployment metadata: - name: romm - namespace: romm + name: romm + namespace: romm spec: - template: - spec: - enableServiceLinks: false # ← this line + template: + spec: + enableServiceLinks: false # ← this line ``` Covered in full in the [Kubernetes install guide](../install/kubernetes.md#required-quirk-enableservicelinks-false). @@ -33,16 +33,16 @@ Add the annotation: ```yaml metadata: - annotations: - nginx.ingress.kubernetes.io/proxy-body-size: "0" + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: "0" ``` Traefik equivalent: ```yaml metadata: - annotations: - traefik.ingress.kubernetes.io/request-maxsize: "0" + annotations: + traefik.ingress.kubernetes.io/request-maxsize: "0" ``` Cloudflare (when in front of your ingress): check plan limits. Free tier caps uploads at 100 MB regardless of what your cluster allows. @@ -55,9 +55,9 @@ nginx-ingress: ```yaml metadata: - annotations: - nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" - nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + annotations: + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" ``` ## Pod crashes on startup: `permission denied` writing to `/romm/resources` @@ -70,16 +70,21 @@ Two fixes: ```yaml initContainers: - - name: fix-permissions - image: busybox - command: ["sh", "-c", "chown -R 1000:1000 /romm/resources /romm/assets /romm/config /redis-data"] - volumeMounts: - - { name: resources, mountPath: /romm/resources } - - { name: assets, mountPath: /romm/assets } - - { name: config, mountPath: /romm/config } - - { name: redis-data, mountPath: /redis-data } - securityContext: - runAsUser: 0 + - name: fix-permissions + image: busybox + command: + [ + "sh", + "-c", + "chown -R 1000:1000 /romm/resources /romm/assets /romm/config /redis-data", + ] + volumeMounts: + - { name: resources, mountPath: /romm/resources } + - { name: assets, mountPath: /romm/assets } + - { name: config, mountPath: /romm/config } + - { name: redis-data, mountPath: /redis-data } + securityContext: + runAsUser: 0 ``` - **Storage class that supports `fsGroup`**: add `fsGroup: 1000` to the pod's `securityContext`. Works on most CSI drivers but not all. @@ -111,10 +116,10 @@ Raise the limit: ```yaml resources: - requests: - memory: "1Gi" - limits: - memory: "4Gi" + requests: + memory: "1Gi" + limits: + memory: "4Gi" ``` Or disable hashing on the Scan page to cut memory use by ~80% (you lose RetroAchievements + Hasheous matching, see [Metadata Providers](../administration/metadata-providers.md)). diff --git a/docs/troubleshooting/miscellaneous.md b/docs/troubleshooting/miscellaneous.md index bae1efb0..aa45e232 100644 --- a/docs/troubleshooting/miscellaneous.md +++ b/docs/troubleshooting/miscellaneous.md @@ -13,7 +13,7 @@ Mount the database file to persistent storage: ```yaml volumes: - - /path/to/database:/romm/database + - /path/to/database:/romm/database ``` This only applies to SQLite. MariaDB / MySQL / Postgres users persist via the DB container's own volume (`mysql_data:/var/lib/mysql` and similar), not via a `/romm/database` mount. diff --git a/docs/troubleshooting/scanning.md b/docs/troubleshooting/scanning.md index 2a7f31d6..64e68c01 100644 --- a/docs/troubleshooting/scanning.md +++ b/docs/troubleshooting/scanning.md @@ -29,8 +29,8 @@ Common mount-path mistakes: - /server/media/games:/romm/library # CORRECT: mounts the parent of roms/ -- /server/media/library:/romm/library # if library/ contains roms/ -- /server/media/games/roms:/romm/library/roms # if you want to be explicit +- /server/media/library:/romm/library # if library/ contains roms/ +- /server/media/games/roms:/romm/library/roms # if you want to be explicit ``` ## Platform folder isn't detected @@ -46,8 +46,8 @@ Two fixes: ```yaml system: - platforms: - n64dd: "64dd" # your-folder-name: canonical-slug + platforms: + n64dd: "64dd" # your-folder-name: canonical-slug ``` Full list of supported slugs: [Supported Platforms](../platforms/supported-platforms.md). diff --git a/docs/troubleshooting/synology.md b/docs/troubleshooting/synology.md index c744ccdb..1b0a1692 100644 --- a/docs/troubleshooting/synology.md +++ b/docs/troubleshooting/synology.md @@ -31,6 +31,7 @@ The usual Synology permission issue. Fix via SSH: Scans should now complete cleanly. + !!! tip If you're still getting permission errors *inside* the container after this, the UID/GID running inside the container doesn't match the host files. Either change the container's `user:` directive to match your Synology user, or change the host file ownership to match the container's expected UID. @@ -59,7 +60,7 @@ DSM occasionally rotates Docker's data directory or the BTRFS subvolume paths af Fix: 1. Don't panic. Your host-mounted paths (`/volume1/...`) aren't touched. -2. Check your compose file. Any volumes declared as *named* Docker volumes (as opposed to host bind mounts) might have been recreated empty. +2. Check your compose file. Any volumes declared as _named_ Docker volumes (as opposed to host bind mounts) might have been recreated empty. 3. Prefer host bind mounts on Synology specifically: `/volume1/docker/romm/resources` instead of a named `romm_resources` volume. The [Synology install guide](../install/synology.md) uses this pattern. ## Still stuck diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index 255ec45e..5a7811fa 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -7,7 +7,7 @@ description: Manage your RomM account: profile, password, avatar, API tokens, de Your account controls: profile drawer → **Profile**. -Every user (Viewer, Editor, Admin) can manage their own profile. Admins can edit *other* users' profiles. See [Users & Roles](../administration/users-and-roles.md) for the admin side. +Every user (Viewer, Editor, Admin) can manage their own profile. Admins can edit _other_ users' profiles. See [Users & Roles](../administration/users-and-roles.md) for the admin side. ## Profile basics @@ -54,7 +54,6 @@ Token format: `rmm_` + 40 hex chars, and you should treat it like a password. For handheld apps like Grout, typing a 44-character token on a thumbstick isn't realistic. Instead: - 1. **Profile → Client API Tokens → [token] → Pair Device**. 2. A short numeric code appears (8 digits, valid for 5 minutes). 3. On the device (Grout app, etc.): enter the code. @@ -76,15 +75,15 @@ Full flow in [Client API Tokens](../ecosystem/client-api-tokens.md). Available throughout the app: -| Key | Action | -| --- | --- | -| `/` | Focus the search bar. | -| `Esc` | Close dialog, drawer, or player. | -| `g h` | Go to home dashboard. | -| `g s` | Open search page. | -| `g c` | Focus the scan / admin action in the sidebar. | -| `j` / `k` | Next / previous item in a list view. | -| `Enter` | Activate focused item. | +| Key | Action | +| --------- | --------------------------------------------- | +| `/` | Focus the search bar. | +| `Esc` | Close dialog, drawer, or player. | +| `g h` | Go to home dashboard. | +| `g s` | Open search page. | +| `g c` | Focus the scan / admin action in the sidebar. | +| `j` / `k` | Next / previous item in a list view. | +| `Enter` | Activate focused item. | ## Accessibility diff --git a/docs/using/collections.md b/docs/using/collections.md index 42ce5ea2..9e767b1c 100644 --- a/docs/using/collections.md +++ b/docs/using/collections.md @@ -72,13 +72,13 @@ Deletion only removes the collection itself. The ROMs inside it stay in the libr ## Who can do what -| Action | Viewer | Editor | Admin | -| --- | :---: | :---: | :---: | -| See public collections | ✓ | ✓ | ✓ | -| Create own private collection | ✓ | ✓ | ✓ | -| Edit own collection | ✓ | ✓ | ✓ | -| Make a collection public | - | ✓ | ✓ | -| See all users' collections (admin panel) | - | - | ✓ | +| Action | Viewer | Editor | Admin | +| ---------------------------------------- | :----: | :----: | :---: | +| See public collections | ✓ | ✓ | ✓ | +| Create own private collection | ✓ | ✓ | ✓ | +| Edit own collection | ✓ | ✓ | ✓ | +| Make a collection public | - | ✓ | ✓ | +| See all users' collections (admin panel) | - | - | ✓ | Scope mapping and the full permission matrix in [Users & Roles](../administration/users-and-roles.md#scope-matrix). diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md index 348fa2f6..f2decc62 100644 --- a/docs/using/console-mode.md +++ b/docs/using/console-mode.md @@ -33,17 +33,17 @@ Standard gamepad API. Xbox, PS, 8BitDo, Steam Controller, Switch Pro: anything t Default bindings (most platforms, configurable): -| Button | Action | -| --- | --- | -| D-pad / left stick | Move focus | -| A (bottom) | Activate | -| B (right) | Back / cancel | -| X (top) | Play | -| Y (left) | Details / context menu | -| LB / RB | Switch tabs or scroll by page | -| LT / RT | Jump to start / end of grid | -| Start | Main menu | -| Select / Back | Filters | +| Button | Action | +| ------------------ | ----------------------------- | +| D-pad / left stick | Move focus | +| A (bottom) | Activate | +| B (right) | Back / cancel | +| X (top) | Play | +| Y (left) | Details / context menu | +| LB / RB | Switch tabs or scroll by page | +| LT / RT | Jump to start / end of grid | +| Start | Main menu | +| Select / Back | Filters | Button labels follow the Xbox convention regardless of actual controller: "A" is always the bottom face button. @@ -51,13 +51,13 @@ Button labels follow the Xbox convention regardless of actual controller: "A" is If you're browsing `/console` without a gamepad for some reason: -| Key | Action | -| --- | --- | -| Arrow keys | Move focus | -| Enter / Space | Activate | -| Escape | Back | -| `/` | Search | -| `f` | Filters | +| Key | Action | +| ------------- | ---------- | +| Arrow keys | Move focus | +| Enter / Space | Activate | +| Escape | Back | +| `/` | Search | +| `f` | Filters | ### Touch @@ -77,7 +77,6 @@ Subtle SFX play on focus, activate, and back. Turn off in **Settings → Console Cards and buttons are sized for 10-foot viewing. Grids show fewer items per row than the main UI. - ### Simpler information density The game detail view uses full-screen tabs instead of side-by-side panels. Filter panels are dedicated screens rather than popovers. diff --git a/docs/using/downloads.md b/docs/using/downloads.md index 2bfcdd7d..d95590a8 100644 --- a/docs/using/downloads.md +++ b/docs/using/downloads.md @@ -1,6 +1,6 @@ --- title: Downloads -description: Download ROMs from RomM: single, bulk, QR codes, copy-link, and streaming for third-party apps. +description: Download ROMs from RomM to your local device --- # Downloads @@ -59,11 +59,12 @@ Some third-party tools (a dumb emulator loading a ROM by URL, a browser extensio ```yaml environment: - - DISABLE_DOWNLOAD_ENDPOINT_AUTH=true + - DISABLE_DOWNLOAD_ENDPOINT_AUTH=true ``` This makes ROM and firmware download URLs work unauthenticated. + !!! danger "Only enable this behind upstream auth" This flag makes your library world-downloadable from whatever URL RomM lives at. Only set it when you have authentication at the reverse-proxy layer (Authelia, Cloudflare Access, an IP allowlist, a VPN). diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md index 9508adc5..9a1a3bc0 100644 --- a/docs/using/in-browser-play.md +++ b/docs/using/in-browser-play.md @@ -16,38 +16,40 @@ Hit the **Play** button on any supported game, and the emulator loads full-scree [EmulatorJS](https://emulatorjs.org/) is a web-based emulator running [RetroArch](https://www.retroarch.com) via [Emscripten](https://emscripten.org/), so cores you know from RetroArch show up here. + !!! warning "Emulation is resource-intensive" Older or less-powerful devices may struggle, especially with demanding cores (Dreamcast, Saturn, PSP). Try a different browser or device before filing a bug. + !!! note "Some cores don't work in Console Mode" PSP (`ppsspp`) and MS-DOS (`dosbox-pure`) aren't supported in [Console Mode](console-mode.md). Use the main UI for those. ### Supported platforms -| Platform | Cores | -| --- | --- | -| 3DO | `opera` | -| Amiga | `puae` | -| Arcade / MAME | `mame2003_plus`, `mame2003`, `fbneo` | -| Atari 2600 / 5200 / 7800 / Jaguar / Lynx | Various: `stella2014`, `atari800`, `prosystem`, `virtualjaguar`, `handy` | -| Commodore 64 | `vice_x64sc` | -| ColecoVision | `gearcoleco` | -| DOOM | `prboom` | -| Game Boy / Color / Advance | `gambatte`, `mgba` | -| MS-DOS | `dosbox-pure` | -| Neo Geo Pocket / Color | `mednafen_ngp` | -| Nintendo DS | `melonds`, `desmume` | -| Nintendo 64 | `mupen64plus_next`, `parallel_n64` | -| NES / Famicom | `fceumm`, `nestopia` | -| PC-FX | `mednafen_pcfx` | -| PlayStation | `mednafen_psx_hw`, `pcsx_rearmed` | -| PSP | `ppsspp` | -| Sega 32X / CD / Game Gear / Master System / Genesis | `picodrive`, `genesis_plus_gx` | -| Sega Saturn | `mednafen_saturn`, `yabause` | -| SNES / Super Famicom | `snes9x`, `bsnes` | -| TurboGraphx-16 / PC Engine | `mednafen_pce` | -| Virtual Boy | `mednafen_vb` | -| WonderSwan / Color | `mednafen_wswan` | +| Platform | Cores | +| --------------------------------------------------- | ------------------------------------------------------------------------ | +| 3DO | `opera` | +| Amiga | `puae` | +| Arcade / MAME | `mame2003_plus`, `mame2003`, `fbneo` | +| Atari 2600 / 5200 / 7800 / Jaguar / Lynx | Various: `stella2014`, `atari800`, `prosystem`, `virtualjaguar`, `handy` | +| Commodore 64 | `vice_x64sc` | +| ColecoVision | `gearcoleco` | +| DOOM | `prboom` | +| Game Boy / Color / Advance | `gambatte`, `mgba` | +| MS-DOS | `dosbox-pure` | +| Neo Geo Pocket / Color | `mednafen_ngp` | +| Nintendo DS | `melonds`, `desmume` | +| Nintendo 64 | `mupen64plus_next`, `parallel_n64` | +| NES / Famicom | `fceumm`, `nestopia` | +| PC-FX | `mednafen_pcfx` | +| PlayStation | `mednafen_psx_hw`, `pcsx_rearmed` | +| PSP | `ppsspp` | +| Sega 32X / CD / Game Gear / Master System / Genesis | `picodrive`, `genesis_plus_gx` | +| Sega Saturn | `mednafen_saturn`, `yabause` | +| SNES / Super Famicom | `snes9x`, `bsnes` | +| TurboGraphx-16 / PC Engine | `mednafen_pce` | +| Virtual Boy | `mednafen_vb` | +| WonderSwan / Color | `mednafen_wswan` | Full up-to-date list: [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/). @@ -55,6 +57,7 @@ Full up-to-date list: [EmulatorJS Systems docs](https://emulatorjs.org/docs/syst Some platforms need BIOS / firmware: PlayStation, Saturn, Sega CD, PSP, DS. Upload via [Firmware Management](../administration/firmware-management.md) or by dropping files in the right `bios/` folder and rescanning. + !!! note "Zip bundles for multi-file firmware" Some cores need several BIOS files. Create a **zip archive** containing all of them and upload that zip through the Firmware tab on the platform detail page. EmulatorJS picks it up as a single firmware bundle. See [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/) for the per-platform file list. @@ -123,6 +126,7 @@ See [Netplay](netplay.md). One-page deep dive on hosting/joining, ICE servers, a [Ruffle](https://ruffle.rs/) is a Flash / Shockwave player in WebAssembly. Useful for preserving the Flash game era. + !!! important "Ruffle needs the right platform folder" Ruffle only plays games from platform folders named `flash` or `browser`. If your Flash games are elsewhere, either rename the folder or add a [platform binding](../reference/configuration-file.md#systemplatforms) in `config.yml`. diff --git a/docs/using/languages.md b/docs/using/languages.md index bb236ae2..2b2e63ab 100644 --- a/docs/using/languages.md +++ b/docs/using/languages.md @@ -9,25 +9,25 @@ RomM's UI is translated into 19 locales. Pick yours from **Profile → User Inte ## Supported locales -| Code | Language | -| --- | --- | +| Code | Language | +| ------- | -------------------------------- | | `en_US` | English (United States), default | -| `en_GB` | English (United Kingdom) | -| `es_ES` | Spanish (Spain) | -| `fr_FR` | French | -| `de_DE` | German | -| `it_IT` | Italian | -| `pt_BR` | Portuguese (Brazil) | -| `ja_JP` | Japanese | -| `ko_KR` | Korean | -| `ru_RU` | Russian | -| `pl_PL` | Polish | -| `cs_CZ` | Czech | -| `hu_HU` | Hungarian | -| `ro_RO` | Romanian | -| `bg_BG` | Bulgarian | -| `zh_CN` | Chinese (Simplified) | -| `zh_TW` | Chinese (Traditional) | +| `en_GB` | English (United Kingdom) | +| `es_ES` | Spanish (Spain) | +| `fr_FR` | French | +| `de_DE` | German | +| `it_IT` | Italian | +| `pt_BR` | Portuguese (Brazil) | +| `ja_JP` | Japanese | +| `ko_KR` | Korean | +| `ru_RU` | Russian | +| `pl_PL` | Polish | +| `cs_CZ` | Czech | +| `hu_HU` | Hungarian | +| `ro_RO` | Romanian | +| `bg_BG` | Bulgarian | +| `zh_CN` | Chinese (Simplified) | +| `zh_TW` | Chinese (Traditional) | Two more ship as work-in-progress with partial coverage. Check the language dropdown to see the current list. diff --git a/docs/using/library.md b/docs/using/library.md index 4dadbf13..f27d19d0 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -81,7 +81,7 @@ Dropdowns that cross-reference metadata on the visible set: - **Genre / Franchise / Collections / Company / Age Rating / Region / Language**: metadata dimensions. - **Status**: personal play status (Never Played, Backlogged, Playing, Complete, Hidden). -Filters narrow down *what you're currently viewing*. Search first, filter second, and the filter dropdowns only show values present in the search results. +Filters narrow down _what you're currently viewing_. Search first, filter second, and the filter dropdowns only show values present in the search results. ## Search @@ -156,11 +156,11 @@ Which tabs appear depends on your metadata providers: ## Keyboard shortcuts -| Key | Action | -| --- | --- | -| `/` | Focus search. | -| `g h` | Go home. | -| `g s` | Open Search page. | +| Key | Action | +| ----- | ---------------------------- | +| `/` | Focus search. | +| `g h` | Go home. | +| `g s` | Open Search page. | | `Esc` | Close open drawer or dialog. | Full shortcut list is on the [Account & Profile](account-and-profile.md) page. diff --git a/docs/using/mobile-and-tv.md b/docs/using/mobile-and-tv.md index d0c2f0fc..5adcd0b4 100644 --- a/docs/using/mobile-and-tv.md +++ b/docs/using/mobile-and-tv.md @@ -82,21 +82,20 @@ Any browser works. Use the PWA install flow (same as desktop) for a dedicated la Syncs ROMs and saves to the Deck's local library so RetroArch / EmuDeck picks them up natively. Set up once, then play without RomM in the loop. See [Community Apps](../ecosystem/community-apps.md). - Best combo: DeckRommSync for saves, plus the PWA for browsing / managing the library. ## Bandwidth considerations Some things are bandwidth-hungry, some aren't: -| Activity | Typical bandwidth | Notes | -| --- | --- | --- | -| Browsing the library | Low | Cover-art thumbnails, a few hundred KB per page load. | -| Playing in browser | Medium | ROM streams at boot, then cached. PSP / Saturn ISOs can be hundreds of MB on first load. | -| Netplay | Medium–High | Video stream from host to players. ~500 kbps for SNES, more for higher-res cores. | -| Bulk download | As-much-as-you-want | Rate-limited only by your reverse proxy and network. | -| Device sync (saves) | Low | Saves are small, so sync is fast. | -| Device sync (ROMs) | High | Pushing full ROM sets to a handheld initially is a lot. | +| Activity | Typical bandwidth | Notes | +| -------------------- | ------------------- | ---------------------------------------------------------------------------------------- | +| Browsing the library | Low | Cover-art thumbnails, a few hundred KB per page load. | +| Playing in browser | Medium | ROM streams at boot, then cached. PSP / Saturn ISOs can be hundreds of MB on first load. | +| Netplay | Medium–High | Video stream from host to players. ~500 kbps for SNES, more for higher-res cores. | +| Bulk download | As-much-as-you-want | Rate-limited only by your reverse proxy and network. | +| Device sync (saves) | Low | Saves are small, so sync is fast. | +| Device sync (ROMs) | High | Pushing full ROM sets to a handheld initially is a lot. | On cellular? Set `DISABLE_DOWNLOAD_ENDPOINT_AUTH=false` (default: keep auth on) to avoid accidental discovery, and prefer native companion apps over in-browser play. diff --git a/docs/using/netplay.md b/docs/using/netplay.md index c68f4531..73015f69 100644 --- a/docs/using/netplay.md +++ b/docs/using/netplay.md @@ -9,6 +9,7 @@ description: Play EmulatorJS games with friends in real time: hosting, joining, Best for 2-player co-op and turn-based games. Not ideal for twitchy fighting games (frame-level input isn't rollback-based). + !!! note "Netplay is a v5.0 feature" If you're on an older RomM (4.x), Netplay was disabled with known issues. 5.0 brings it back as a supported feature. @@ -54,19 +55,19 @@ The operator wires ICE servers in `config.yml`: ```yaml emulatorjs: - netplay: - enabled: true - ice_servers: - # Google's free public STUN, no account needed - - urls: "stun:stun.l.google.com:19302" - - urls: "stun:stun1.l.google.com:19302" - # OpenRelay free TURN (rate-limited) - - urls: "turn:openrelay.metered.ca:80" - username: "openrelayproject" - credential: "openrelayproject" - - urls: "turn:openrelay.metered.ca:443" - username: "openrelayproject" - credential: "openrelayproject" + netplay: + enabled: true + ice_servers: + # Google's free public STUN, no account needed + - urls: "stun:stun.l.google.com:19302" + - urls: "stun:stun1.l.google.com:19302" + # OpenRelay free TURN (rate-limited) + - urls: "turn:openrelay.metered.ca:80" + username: "openrelayproject" + credential: "openrelayproject" + - urls: "turn:openrelay.metered.ca:443" + username: "openrelayproject" + credential: "openrelayproject" ``` For production Netplay, operators should get a dedicated TURN account (free tier at [Metered](https://www.metered.ca/stun-turn), or self-host [coturn](https://github.com/coturn/coturn)). diff --git a/docs/using/retroachievements.md b/docs/using/retroachievements.md index 5de360c6..342950e9 100644 --- a/docs/using/retroachievements.md +++ b/docs/using/retroachievements.md @@ -79,7 +79,7 @@ Some tips: ## Privacy - Your RA API key is stored server-side (per-user), encrypted at rest. -- RomM only calls RA using your key for *your* data, never shares across users. +- RomM only calls RA using your key for _your_ data, never shares across users. - Admins can see which users have RA linked but not the API keys themselves. To unlink: Profile → RetroAchievements → **Unlink** → confirm. Key is deleted. Progression data stays cached until the next sync pass. diff --git a/docs/using/rom-patcher.md b/docs/using/rom-patcher.md index b2d6e596..16c41af7 100644 --- a/docs/using/rom-patcher.md +++ b/docs/using/rom-patcher.md @@ -11,17 +11,17 @@ New in 5.0. Powered by the `rom-patcher-js` library. ## Supported patch formats -| Extension | Purpose | -| --- | --- | -| `.ips` | International Patching System. Oldest + most common. | -| `.ups` | Universal Patching System. Successor to IPS. | -| `.bps` | Binary Patch System. Modern, preferred for ROM hacks. | -| `.ppf` | PlayStation Patch Format. For PSX / Saturn / Dreamcast ISOs. | -| `.rup` | Retroarch Universal Patch. | -| `.aps` | GBA-focused patch format. | -| `.bdf` | Binary diff format. | -| `.pmsr` | Paper Mario Star Rod. | -| `.vcdiff` (`.xdelta`) | Generic binary diff. | +| Extension | Purpose | +| --------------------- | ------------------------------------------------------------ | +| `.ips` | International Patching System. Oldest + most common. | +| `.ups` | Universal Patching System. Successor to IPS. | +| `.bps` | Binary Patch System. Modern, preferred for ROM hacks. | +| `.ppf` | PlayStation Patch Format. For PSX / Saturn / Dreamcast ISOs. | +| `.rup` | Retroarch Universal Patch. | +| `.aps` | GBA-focused patch format. | +| `.bdf` | Binary diff format. | +| `.pmsr` | Paper Mario Star Rod. | +| `.vcdiff` (`.xdelta`) | Generic binary diff. | If your patch has an unusual extension, try renaming to one of the above. Many are just different framings of the same underlying algorithm. @@ -50,10 +50,10 @@ Metadata isn't inherited, so the new ROM is **unmatched** until you run a scan. ## Permissions -| Action | Viewer | Editor | Admin | -| --- | :---: | :---: | :---: | -| Use Patcher → Download | ✓ | ✓ | ✓ | -| Use Patcher → Save to library | - | ✓ | ✓ | +| Action | Viewer | Editor | Admin | +| ----------------------------- | :----: | :----: | :---: | +| Use Patcher → Download | ✓ | ✓ | ✓ | +| Use Patcher → Save to library | - | ✓ | ✓ | Saving to the library requires `roms.write` scope. diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md index 47ff586a..482bc551 100644 --- a/docs/using/saves-and-states.md +++ b/docs/using/saves-and-states.md @@ -82,15 +82,15 @@ From the end-user side: once your device is paired and sync is running, saves ma Save files are usually format-interchangeable across cores for the same platform, but not always. -| Platform | Format | Usually-compatible | -| --- | --- | --- | -| NES | `.sav` | Yes, across FCEUmm / Nestopia | -| SNES | `.srm` | Yes, across SNES9x / bsnes | -| Genesis / Mega Drive | `.srm` | Yes | -| Game Boy / GBC / GBA | `.sav` | Yes, across Gambatte / mGBA | -| N64 | `.srm`, `.eep`, `.fla` | Yes, but per-type: the right file must be uploaded | -| PSX | `.srm` (memory card) | Yes, across Mednafen / PCSX cores | -| Saturn / Dreamcast | Varies | Check core docs | +| Platform | Format | Usually-compatible | +| -------------------- | ---------------------- | -------------------------------------------------- | +| NES | `.sav` | Yes, across FCEUmm / Nestopia | +| SNES | `.srm` | Yes, across SNES9x / bsnes | +| Genesis / Mega Drive | `.srm` | Yes | +| Game Boy / GBC / GBA | `.sav` | Yes, across Gambatte / mGBA | +| N64 | `.srm`, `.eep`, `.fla` | Yes, but per-type: the right file must be uploaded | +| PSX | `.srm` (memory card) | Yes, across Mednafen / PCSX cores | +| Saturn / Dreamcast | Varies | Check core docs | If you're moving saves between RomM's EmulatorJS and a stand-alone emulator (RetroArch, Dolphin, PPSSPP), usually works for mainline cores. diff --git a/docs/using/smart-collections.md b/docs/using/smart-collections.md index a05ea0d0..6a08d531 100644 --- a/docs/using/smart-collections.md +++ b/docs/using/smart-collections.md @@ -63,14 +63,14 @@ Each condition has three parts: Available operators depend on the field type: -| Operator | Works with | -| --- | --- | -| `equals`, `not equals` | Everything. | -| `contains`, `does not contain` | Text fields. | -| `starts with`, `ends with` | Text fields. | -| `is`, `is not` | Boolean / enum fields (Status, Matched, Favourites). | -| `greater than`, `less than`, `between` | Numeric fields (Rating, Playtime, Release Year). | -| `in`, `not in` | Multi-value fields (Region, Language, Genre). | +| Operator | Works with | +| -------------------------------------- | ---------------------------------------------------- | +| `equals`, `not equals` | Everything. | +| `contains`, `does not contain` | Text fields. | +| `starts with`, `ends with` | Text fields. | +| `is`, `is not` | Boolean / enum fields (Status, Matched, Favourites). | +| `greater than`, `less than`, `between` | Numeric fields (Rating, Playtime, Release Year). | +| `in`, `not in` | Multi-value fields (Region, Language, Genre). | ## Examples @@ -115,7 +115,7 @@ any of: Same visibility model as standard collections: - **Private**: only you see it (your personal data fields matter). -- **Public**: everyone on the instance sees it. *Your* personal-data rules still apply. If your smart collection is "games I haven't finished", every user sees *your* unfinished games. +- **Public**: everyone on the instance sees it. _Your_ personal-data rules still apply. If your smart collection is "games I haven't finished", every user sees _your_ unfinished games. For shared rule sets across users, use metadata-only fields and keep the collection public. Rules that reference Personal data (status, rating, playtime, favourites) only make sense as private collections. diff --git a/docs/using/uploads.md b/docs/using/uploads.md index 4cc790f6..34077f60 100644 --- a/docs/using/uploads.md +++ b/docs/using/uploads.md @@ -7,15 +7,15 @@ description: Upload ROMs, firmware, saves, states, and screenshots into RomM fro ## What you can upload -| Type | Permission | Where | Details | -| --- | --- | --- | --- | -| **ROMs** | Admin, Editor | **Upload** in menu bar, **Upload ROM** on platform detail, ROM detail → Upload menu | Goes to the correct platform folder in `/romm/library`. | -| **Firmware / BIOS** | Admin, Editor | Platform detail → Firmware button → Upload | See [Firmware Management](../administration/firmware-management.md). | -| **Saves** | Self (own games) | Game detail → Game Data tab → Upload Save | Per-ROM, per-user. | -| **States** | Self (own games) | Game detail → Game Data tab → Upload State | Per-ROM, per-user. Optional screenshot attached. | -| **Screenshots** | Self (own games) | Game detail → Screenshots tab → Upload | Per-ROM, per-user. | -| **Manuals** | Admin, Editor | Game detail → Manual tab → Upload (when empty) | PDF, becomes the Manual tab content. | -| **Cover art** | Admin, Editor | Game detail → Edit → Upload cover | Replaces the provider-fetched cover. | +| Type | Permission | Where | Details | +| ------------------- | ---------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| **ROMs** | Admin, Editor | **Upload** in menu bar, **Upload ROM** on platform detail, ROM detail → Upload menu | Goes to the correct platform folder in `/romm/library`. | +| **Firmware / BIOS** | Admin, Editor | Platform detail → Firmware button → Upload | See [Firmware Management](../administration/firmware-management.md). | +| **Saves** | Self (own games) | Game detail → Game Data tab → Upload Save | Per-ROM, per-user. | +| **States** | Self (own games) | Game detail → Game Data tab → Upload State | Per-ROM, per-user. Optional screenshot attached. | +| **Screenshots** | Self (own games) | Game detail → Screenshots tab → Upload | Per-ROM, per-user. | +| **Manuals** | Admin, Editor | Game detail → Manual tab → Upload (when empty) | PDF, becomes the Manual tab content. | +| **Cover art** | Admin, Editor | Game detail → Edit → Upload cover | Replaces the provider-fetched cover. | ## ROM upload @@ -97,12 +97,12 @@ Admin / Editor. Game detail → Context menu (…) → **Edit** → **Upload cov ## Permissions summary -| Action | Viewer | Editor | Admin | -| --- | :---: | :---: | :---: | -| Upload save/state/screenshot for own account | ✓ | ✓ | ✓ | -| Upload ROMs | - | ✓ | ✓ | -| Upload firmware | - | ✓ | ✓ | -| Upload manual / cover art | - | ✓ | ✓ | +| Action | Viewer | Editor | Admin | +| -------------------------------------------- | :----: | :----: | :---: | +| Upload save/state/screenshot for own account | ✓ | ✓ | ✓ | +| Upload ROMs | - | ✓ | ✓ | +| Upload firmware | - | ✓ | ✓ | +| Upload manual / cover art | - | ✓ | ✓ | Full scope matrix in [Users & Roles](../administration/users-and-roles.md#scope-matrix). diff --git a/docs/using/virtual-collections.md b/docs/using/virtual-collections.md index 755a8fdd..b1f5df66 100644 --- a/docs/using/virtual-collections.md +++ b/docs/using/virtual-collections.md @@ -11,23 +11,23 @@ New in 5.0. Compare with: - [Collections](collections.md): you pick each ROM by hand. - [Smart Collections](smart-collections.md): you write rules, RomM populates. -- **Virtual Collections**: RomM picks both the groupings *and* the contents. +- **Virtual Collections**: RomM picks both the groupings _and_ the contents. ## What gets grouped Virtual collections are generated for several dimensions: -| Dimension | Example collections | -| --- | --- | -| **Genre** | "Platformer", "RPG", "Shooter", "Puzzle", "Fighting" | -| **Developer** | "Nintendo EAD", "Capcom", "Konami", "id Software" | -| **Publisher** | "Nintendo", "Sega", "Sony" | -| **Franchise** | "The Legend of Zelda", "Final Fantasy", "Mega Man" | -| **Release Year** | "1995", "2003", etc. | -| **Decade** | "1980s", "1990s", "2000s" | -| **Age Rating** | "ESRB: E", "PEGI: 7+" | -| **Region** | "USA", "Japan", "Europe" | -| **Tags** | Any `[]`/`()` tag from filenames, see [Folder Structure → Naming convention](../getting-started/folder-structure.md#naming-convention) | +| Dimension | Example collections | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| **Genre** | "Platformer", "RPG", "Shooter", "Puzzle", "Fighting" | +| **Developer** | "Nintendo EAD", "Capcom", "Konami", "id Software" | +| **Publisher** | "Nintendo", "Sega", "Sony" | +| **Franchise** | "The Legend of Zelda", "Final Fantasy", "Mega Man" | +| **Release Year** | "1995", "2003", etc. | +| **Decade** | "1980s", "1990s", "2000s" | +| **Age Rating** | "ESRB: E", "PEGI: 7+" | +| **Region** | "USA", "Japan", "Europe" | +| **Tags** | Any `[]`/`()` tag from filenames, see [Folder Structure → Naming convention](../getting-started/folder-structure.md#naming-convention) | Collections with too few ROMs are suppressed, so you won't see a "Developer: Some Indie Studio" with one entry. @@ -49,7 +49,7 @@ Virtual collections show up alongside regular and smart collections in: Open one like any collection: grid/list view, filters, play, download, etc. -## What you *can't* do +## What you _can't_ do Virtual collections are read-only. You can't: From 266c0f4c24f4d222d1a9a94421999bbd72c3948f Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 14:47:19 -0400 Subject: [PATCH 040/121] finish about --- CODE_OF_CONDUCT.md | 2 +- LICENSE | 4 +- docs/about/brand-guidelines.md | 2 +- docs/about/credits.md | 82 ++++++------- docs/about/faqs.md | 127 ++++++++++++++------- docs/about/license.md | 2 +- docs/administration/administration-page.md | 2 +- docs/administration/authentication.md | 6 +- docs/administration/firmware-management.md | 2 +- docs/administration/metadata-providers.md | 2 +- docs/developers/api-authentication.md | 4 +- docs/developers/api-reference.md | 2 +- docs/developers/contributing.md | 4 +- docs/developers/i18n.md | 2 +- docs/developers/openapi.md | 2 +- docs/ecosystem/client-api-tokens.md | 4 +- docs/ecosystem/fpkgi.md | 2 +- docs/ecosystem/grout.md | 4 +- docs/ecosystem/igir.md | 2 +- docs/ecosystem/index.md | 4 +- docs/ecosystem/kekatsu.md | 2 +- docs/ecosystem/pkgj.md | 2 +- docs/ecosystem/webrcade.md | 4 +- docs/getting-started/concepts.md | 4 +- docs/getting-started/first-scan.md | 2 +- docs/getting-started/quick-start.md | 4 +- docs/install/backup-and-restore.md | 6 +- docs/install/databases.md | 4 +- docs/install/docker-compose.md | 4 +- docs/install/kubernetes.md | 2 +- docs/install/unraid.md | 2 +- docs/platforms/custom-platforms.md | 2 +- docs/platforms/emulatorjs-config.md | 2 +- docs/platforms/firmware-by-platform.md | 2 +- docs/platforms/ms-dos.md | 2 +- docs/platforms/ruffle-config.md | 2 +- docs/reference/environment-variables.md | 2 +- docs/reference/glossary.md | 2 +- docs/reference/ports-and-endpoints.md | 2 +- docs/releases/index.md | 4 +- docs/troubleshooting/authentication.md | 2 +- docs/troubleshooting/in-browser-play.md | 4 +- docs/troubleshooting/netplay.md | 4 +- docs/troubleshooting/sync.md | 2 +- docs/using/account-and-profile.md | 4 +- docs/using/collections.md | 2 +- docs/using/console-mode.md | 6 +- docs/using/in-browser-play.md | 4 +- docs/using/languages.md | 2 +- docs/using/library.md | 2 +- docs/using/pwa.md | 4 +- docs/using/retroachievements.md | 4 +- docs/using/rom-patcher.md | 2 +- docs/using/saves-and-states.md | 6 +- docs/using/smart-collections.md | 4 +- docs/using/uploads.md | 2 +- 56 files changed, 202 insertions(+), 169 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index dc649691..9a2e52c0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -22,7 +22,7 @@ community include: - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -- Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals but for the overall community Examples of unacceptable behavior include: diff --git a/LICENSE b/LICENSE index 0e259d42..e6ef5e61 100644 --- a/LICENSE +++ b/LICENSE @@ -39,7 +39,7 @@ Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not +Related Rights"). Copyright and Related Rights include but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, @@ -60,7 +60,7 @@ vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. -2. Waiver. To the greatest extent permitted by, but not in contravention +2. Waiver. To the greatest extent permitted by but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes diff --git a/docs/about/brand-guidelines.md b/docs/about/brand-guidelines.md index 494e9d41..b2b6baf7 100644 --- a/docs/about/brand-guidelines.md +++ b/docs/about/brand-guidelines.md @@ -7,7 +7,7 @@ description: How to use the RomM name, logo, and colors. # Brand Guidelines -Guidelines for anyone who wants to use the RomM name and logo. In this context, "RomM", "The RomM Project", "the project", "we", "us", and "our" refer to the RomM project. +In this context, "RomM", "The RomM Project", "the project", "we", "us", and "our" refer to the RomM project and organization. ## The logo diff --git a/docs/about/credits.md b/docs/about/credits.md index 7d644bec..4ca4fd58 100644 --- a/docs/about/credits.md +++ b/docs/about/credits.md @@ -7,10 +7,6 @@ description: The humans, projects, and services that make RomM possible. RomM exists because a lot of people contributed code, designs, translations, ideas, and, most importantly, running bug reports back to the project. Thanks to every one of them. -## Core maintainers - -The small team that reviews PRs, cuts releases, and keeps the project moving. Check [rommapp/romm](https://github.com/rommapp/romm/graphs/contributors) for the current active list. Credits here would drift. - ## Contributors [Every contributor](https://github.com/rommapp/romm/graphs/contributors) to [rommapp/romm](https://github.com/rommapp/romm), plus contributors to the surrounding repos: @@ -18,7 +14,6 @@ The small team that reviews PRs, cuts releases, and keeps the project moving. Ch - [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher/graphs/contributors) - [rommapp/grout](https://github.com/rommapp/grout/graphs/contributors) - [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin/graphs/contributors) -- [rommapp/muos-app](https://github.com/rommapp/muos-app/graphs/contributors) - [rommapp/docs](https://github.com/rommapp/docs/graphs/contributors) ## Translators @@ -29,51 +24,55 @@ RomM's 19 locales exist because individual community members took the time to tr Built by the community, not the RomM team. Full list in [Community Apps](../ecosystem/community-apps.md). +## Financial supporters + +Donors via [Open Collective](https://opencollective.com/romm) make continued development possible. The project wouldn't exist without you. Thank you! ❤️ + ## Upstream projects RomM stands on an enormous amount of open-source work. In rough order of "how visible they are to users": ### In-browser emulation -- [EmulatorJS](https://emulatorjs.org/): the retro emulator that powers most of our in-browser play. -- [Ruffle](https://ruffle.rs/): the Flash / Shockwave emulator. -- [dosbox-pure](https://github.com/schellingb/dosbox-pure): DOS emulation core via EmulatorJS. +- [EmulatorJS](https://emulatorjs.org/) +- [Ruffle](https://ruffle.rs/) +- [dosbox-pure](https://github.com/schellingb/dosbox-pure) ### Metadata sources -- [IGDB](https://www.igdb.com/): the Internet Game Database. -- [ScreenScraper](https://screenscraper.fr/): French community metadata. -- [MobyGames](https://www.mobygames.com/): game database. -- [RetroAchievements](https://retroachievements.org/): achievements and hash matching. -- [SteamGridDB](https://www.steamgriddb.com/): cover art. -- [Hasheous](https://hasheous.org/): hash-based matching. -- [PlayMatch](https://github.com/RetroRealm/playmatch): community hash service. -- [LaunchBox Games Database](https://gamesdb.launchbox-app.com/): local metadata DB. -- [TheGamesDB](https://thegamesdb.net/): free community DB. -- [Flashpoint Archive](https://flashpointproject.github.io/flashpoint-database/): Flash game preservation. -- [HowLongToBeat](https://howlongtobeat.com/): completion times. -- [Libretro](https://www.libretro.com/): core metadata. +- [IGDB](https://www.igdb.com/) +- [ScreenScraper](https://screenscraper.fr/) +- [MobyGames](https://www.mobygames.com/) +- [RetroAchievements](https://retroachievements.org/) +- [SteamGridDB](https://www.steamgriddb.com/) +- [Hasheous](https://hasheous.org/) +- [PlayMatch](https://github.com/RetroRealm/playmatch) +- [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) +- [TheGamesDB](https://thegamesdb.net/) +- [Flashpoint Archive](https://flashpointproject.github.io/flashpoint-database/) +- [HowLongToBeat](https://howlongtobeat.com/) +- [Libretro](https://www.libretro.com/) ### Backend stack -- [FastAPI](https://fastapi.tiangolo.com/) + [Starlette](https://www.starlette.io/): the web framework. -- [SQLAlchemy](https://www.sqlalchemy.org/) + [Alembic](https://alembic.sqlalchemy.org/): ORM and migrations. -- [RQ](https://python-rq.org/): background job queue. -- [MariaDB](https://mariadb.org/) / [PostgreSQL](https://www.postgresql.org/) / [MySQL](https://www.mysql.com/): database backends. -- [Redis](https://redis.io/) / [Valkey](https://valkey.io/): cache and queue. -- [nginx](https://nginx.org/) with [`mod_zip`](https://github.com/evanmiller/mod_zip): reverse proxy + streaming zip downloads. -- [uv](https://docs.astral.sh/uv/): Python package manager. +- [FastAPI](https://fastapi.tiangolo.com/) + [Starlette](https://www.starlette.io/) +- [SQLAlchemy](https://www.sqlalchemy.org/) + [Alembic](https://alembic.sqlalchemy.org/) +- [RQ](https://python-rq.org/) +- [MariaDB](https://mariadb.org/) / [PostgreSQL](https://www.postgresql.org/) / [MySQL](https://www.mysql.com/) +- [Redis](https://redis.io/) / [Valkey](https://valkey.io/) +- [nginx](https://nginx.org/) with [`mod_zip`](https://github.com/evanmiller/mod_zip) +- [uv](https://docs.astral.sh/uv/) ### Frontend stack -- [Vue 3](https://vuejs.org/): frontend framework. -- [Vuetify](https://vuetifyjs.com/): component library. -- [Pinia](https://pinia.vuejs.org/): state management. -- [Vite](https://vitejs.dev/): build tool. -- [Socket.IO](https://socket.io/): real-time communication. -- [vue-i18n](https://vue-i18n.intlify.dev/): localisation. -- [rom-patcher-js](https://www.marcrobledo.com/RomPatcher.js/): the ROM patcher library. -- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/): PWA support. +- [Vue 3](https://vuejs.org/) +- [Vuetify](https://vuetifyjs.com/) +- [Pinia](https://pinia.vuejs.org/) +- [Vite](https://vitejs.dev/) +- [Socket.IO](https://socket.io/) +- [vue-i18n](https://vue-i18n.intlify.dev/) +- [rom-patcher-js](https://www.marcrobledo.com/RomPatcher.js/) +- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) ### Docs stack @@ -81,20 +80,9 @@ RomM stands on an enormous amount of open-source work. In rough order of "how vi - [mike](https://github.com/jimporter/mike): docs versioning. - [asciinema](https://asciinema.org/): terminal recordings. -## Community - -- [#selfh.st](https://selfh.st/): early visibility for the project. -- [Hacker News](https://news.ycombinator.com/): the launch-day bump. -- [Aikido Security](https://www.aikido.dev/): security audit. -- Everyone who submitted a bug report, pinged Discord, or pushed a typo fix. - -## Financial supporters - -Donors via [Open Collective](https://opencollective.com/romm) make continued development possible. The project wouldn't exist without you. Thanks. - ## Missing? -Open a PR against this page. Credit is cheap, and we'd rather err on the side of naming everyone than leaving someone out. +Open a PR against this page. Credit is cheap, and we want to give it where it's due. ## See also diff --git a/docs/about/faqs.md b/docs/about/faqs.md index 008efd8f..aeb10df6 100644 --- a/docs/about/faqs.md +++ b/docs/about/faqs.md @@ -9,80 +9,126 @@ description: Answers to the questions users most often ask about RomM. A self-hosted ROM manager + player: scan your library, pull metadata, browse a clean UI, play in the browser, sync to handhelds, and run it all on your own hardware. -See [Introduction](../index.md) for the full pitch. +See [Introduction](../index.md) for the full pitch, or [Quick Start](../getting-started/quick-start.md) to stand one up. + + +!!! note "RomM is server software you run on a homelab box or NAS" + It is not a desktop app, and there's no `.exe` or `.appimage`. You'll need basic Linux + Docker skills to set it up. RomM also doesn't provide games or copyrighted material. ## Is it free? -Yes, under [AGPL-3.0](license.md). The core will always be free, other repos in the umbrella use permissive licenses, and there's no tracking or upsells. +Yes! Licensed under [AGPL-3.0](license.md), the core will always be free. Other repos under our umbrella use equally permissive licenses, and there's no tracking or upsells. ## How does it compare to [X other manager]? -Not a direct comparison page, but the short version: RomM emphasises self-hosted + multi-user + in-browser-play + companion-app ecosystem. If those matter, try RomM; if you just want a local Windows app that scans a folder, tools like LaunchBox may fit better. +RomM emphasises self-hosted + multi-user + in-browser-play + the companion-app ecosystem. If those matter, try RomM; if you just want a local Windows app that scans a folder, tools like LaunchBox may fit better. ## Do I need metadata API keys? -Not strictly. RomM runs without any, but games just won't match to a metadata source, so no covers, descriptions, or ratings. +Not strictly. RomM runs without any but games won't match against a metadata source, so no covers, descriptions, or ratings. -Recommended: IGDB + ScreenScraper. See [Metadata Providers](../administration/metadata-providers.md) for the full list. +Hasheous + IGDB + SteamGridDB + RetroAchievements is the recommended set, though many users only run ScreenScraper + Retroachievements (see [Metadata Providers](../administration/metadata-providers.md) for the full list). ## Is RomM legal? -The software is legal, but what you put in it depends on your jurisdiction. We don't ship ROMs or firmware, don't help you find them, and can't give legal advice. General rule: dumping games you own is usually fine, distributing copies is usually not. +The software is legal but what you put in it depends on your jurisdiction. **We don't ship ROMs or firmware, don't help you find them, and can't give legal advice.** ## Can I run RomM on [X]? -Probably. Supported deployment paths: +Probably! Supported deployment paths: -- Docker Compose (Linux, macOS, Windows with WSL). -- Unraid / Synology / TrueNAS SCALE. -- Kubernetes. +- Docker Compose (Linux, macOS, Windows with WSL2) +- Unraid / Synology / TrueNAS SCALE +- Kubernetes See [Install & Deploy](../install/index.md). Not supported: -- Bare metal without containers (not documented, but may work). -- TrueNAS CORE (FreeBSD, no Docker). -- Windows without WSL (Docker Desktop works, but bare Windows doesn't). +- Bare metal without containers (undocumented but _should_ work) +- TrueNAS CORE (FreeBSD) +- Windows without WSL ## How much RAM / CPU does it need? - **Minimum** (small library, 1 user): 512 MB RAM, any modest CPU. - **Comfortable** (thousands of ROMs, a few users, occasional scans): 2 GB RAM, 2 cores. -- **In-browser play**: browser-side resource-heavy, RomM-server-side negligible. -Heaviest CPU is during scans: hashing + network-bound metadata calls. Plan for spikes. +Heaviest CPU usage is during scans, as hashing and network-bound metadata calls may cause spikes. ## How do I update? ```sh -docker compose pull -docker compose up -d +docker compose pull rommapp/romm +docker compose up -d romm ``` -Alembic migrations run automatically. Read the release notes before major versions. See [Release Notes & Migration](../releases/index.md). +Always [read the release notes](../releases/index.md) before minor or major version upgrades! ## Why can't I see a specific platform? -Platform folder name probably doesn't match a known slug. Check [Supported Platforms](../platforms/supported-platforms.md). Fix with either folder rename or a [`system.platforms`](../reference/configuration-file.md#systemplatforms) binding in `config.yml`. +The platform folder name probably doesn't match a known slug. Check [Supported Platforms](../platforms/supported-platforms.md), and fix it by either renaming the folder or updating the [`system.platforms`](../reference/configuration-file.md#systemplatforms) binding in `config.yml`. ## Why are my ROMs unmatched after a scan? Most common reasons: -- No metadata providers configured: enable at least one. -- Filename too generic (no tags, unusual naming): add filename tags like `(igdb-1234)` or try another provider. -- Wrong platform detection: see previous FAQ. +- No metadata providers configured +- Filename too generic (no tags, unusual naming) +- Wrong platform detection + +Full troubleshooting steps can be found in [Scanning Troubleshooting](../troubleshooting/scanning.md). + +## My scan finds platforms but no games inside them. + +This is almost always a mount-depth issue. RomM expects the *parent* of your `roms/` folder mounted to `/romm/library`, not the `roms/` folder itself. If your files live at `/opt/romm/library/roms/gbc/game.gbc`, mount `/opt/romm/library` to `/romm/library`, then re-scan. + +See [Folder Structure](../getting-started/folder-structure.md) and [Scanning Troubleshooting](../troubleshooting/scanning.md) for the full layout and common mount mistakes. + +## Why is my metadata wrong or incomplete? + +RomM doesn't own the metadata, it only pulls from third parties like IGDB and ScreenScraper. If a field is missing or wrong, the fix has to happen upstream on the provider's site. Cross-check against another provider if one is consistently off for your library. + +## Why am I getting a "Configuration file not Mounted!" error? + +RomM uses a `config.yml` in its `/config` folder at startup. If the mount/file is missing or the file is unreadable, it bails with this error. See [Configuration File](../reference/configuration-file.md) for the schema and an example you can drop in. + +## Can I play PS3 or newer consoles in the browser? + +**No.** In-browser emulation handles 4th-gen and earlier systems well. Saturn, PS1, and N64 are hit-or-miss. PSP and newer are mostly unplayable. Browser play is a bonus, as RomM is a library manager first, and standalone emulators remain the right tool for modern systems. See [Supported Platforms](../platforms/supported-platforms.md) for the current list. -Full troubleshooting: [Scanning Troubleshooting](../troubleshooting/scanning.md). +## Why is browser emulation laggy or not loading? + +A few common causes: + +- **Browser**: use a Chromium-based browser (Chrome, Edge, Brave). Firefox and Safari might run EmulatorJS poorly. +- **HTTPS**: PSP and DOS cores require the site served over `https://`. Accessing by IP won't work, so you need a [Reverse Proxy](../install/reverse-proxy.md) with TLS. +- **Hardware**: EmulatorJS runs entirely in the browser, so CPU-heavy cores need a capable (modern) browser. + +Full troubleshooting steps can be found in [In-Browser Play](../troubleshooting/in-browser-play.md). ## Can I share my library with friends? -Yes: add them as users via the invite flow, then either share the URL (if accessible to them) or put RomM behind a VPN / Tailscale. See [Invitations & Registration](../administration/invitations-and-registration.md) + [Mobile & TV → Self-hosting tips](../using/mobile-and-tv.md#self-hosting-tips). +Add them as users via the invite flow, then either: + +- Expose RomM publicly behind a [Reverse Proxy](../install/reverse-proxy.md) with TLS. +- Keep it private and share access over a VPN or Tailscale. + +See [Invitations & Registration](../administration/invitations-and-registration.md) and [Mobile & TV → Self-hosting tips](../using/mobile-and-tv.md#self-hosting-tips). + +## Are there mobile or handheld companion apps? + +Yes, several first-party apps (all in beta or actively developed): + +- **[Argosy](../ecosystem/argosy.md)**: Android launcher for your RomM library +- **[Grout](../ecosystem/grout.md)**: companion for Linux-based handhelds (muOS / NextUI and friends) +- **[Playnite Plugin](../ecosystem/playnite-plugin.md)** for Windows desktop + +iOS and other platforms are covered by [Community Apps](../ecosystem/community-apps.md). Full directory: [Ecosystem](../ecosystem/index.md). ## Can guests browse without an account? -Yes: set `KIOSK_MODE=true`. Anonymous visitors get read-only access. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). +Absolutely, just set `KIOSK_MODE=true` in your environment variables and anonymous visitors get read-only access. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). ## How do I back up? @@ -90,7 +136,7 @@ Yes: set `KIOSK_MODE=true`. Anonymous visitors get read-only access. See [Authen ## Can I use RomM without the internet? -Mostly. You need internet for: +You need internet for: - First-time scan with metadata providers (they're online APIs). - Pulling the Docker image on install or upgrade. @@ -106,32 +152,31 @@ Several possibilities, in rough order of likelihood: 2. Metadata providers rate-limiting (mostly ScreenScraper). 3. Many files on a network mount with high latency. -Full troubleshooting: [Scanning Troubleshooting → Hash calculations are slow](../troubleshooting/scanning.md#hash-calculations-are-slow). +[Scanning Troubleshooting → Hash calculations are slow](../troubleshooting/scanning.md#hash-calculations-are-slow). ## When will [feature X] be added? We don't give ETAs on individual features. Open (or upvote) an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) to make the interest visible. -For public roadmap-ish information, watch the GitHub Projects board and `#announcements` on Discord. - ## What happened to SQLite? -Dropped in 3.0 for stability reasons: use MariaDB, MySQL, or Postgres instead. See [Databases](../install/databases.md). +Dropped in 3.0 for stability reasons; use MariaDB, MySQL, or Postgres instead. See [Databases](../install/databases.md). -## I found a bug. +## I found a bug, or I need help. -Open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues) with: +For bugs, open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues). For questions, ask in `#romm-support` on [Discord](https://discord.gg/romm). Either way, include: -- RomM version. -- Deployment (Docker Compose / Unraid / K8s / etc.). -- Exact repro steps. -- Relevant logs: redact any secrets. +- RomM version +- Deployment (Docker Compose / Unraid / K8s / etc.) +- Your `docker-compose.yml` with credentials and API keys redacted +- Container logs (`docker logs romm`) +- Exact reproduction steps ## Who runs RomM? -A small team of maintainers plus a chunk of active community contributors. AGPL-licensed, community-driven. Support the project via [Open Collective](https://opencollective.com/romm) if you'd like. +A small team of maintainers plus a chunk of active community contributors. Support the project via [Open Collective](https://opencollective.com/romm) if you'd like. -## Where's Discord / GitHub / the website? +## Where's can I find you? - **Discord**: [discord.gg/romm](https://discord.gg/romm) - **GitHub**: [rommapp/romm](https://github.com/rommapp/romm) @@ -141,6 +186,6 @@ A small team of maintainers plus a chunk of active community contributors. AGPL- ## See also -- [Core Concepts](../getting-started/concepts.md): if the vocabulary is new. -- [Troubleshooting](../troubleshooting/index.md): if something's broken. -- [Release Notes](../releases/index.md): for version-specific questions. +- [Core Concepts](../getting-started/concepts.md) if the vocabulary is new +- [Troubleshooting](../troubleshooting/index.md) if something's broken +- [Release Notes](../releases/index.md) for version-specific questions diff --git a/docs/about/license.md b/docs/about/license.md index de16ac93..f27d5705 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -57,7 +57,7 @@ No: AGPL is a strong copyleft, so forks remain AGPL, and while you can add your ### Can I charge money for RomM-as-a-service? -Yes, but you owe your users the source, so you can't run a modified closed-source hosted RomM for paying customers. +Yes but you owe your users the source, so you can't run a modified closed-source hosted RomM for paying customers. ### Can I sell ROMs through RomM? diff --git a/docs/administration/administration-page.md b/docs/administration/administration-page.md index d927e94f..5551e264 100644 --- a/docs/administration/administration-page.md +++ b/docs/administration/administration-page.md @@ -52,7 +52,7 @@ Editor-grade tools for catalogue hygiene. ## Metadata Sources -Admin-only. Per-provider credentials, test buttons, and the priority-ordering drag-and-drop UI. Equivalent to editing the `*_CLIENT_ID` / `*_API_KEY` env vars and `scan.priority` in `config.yml`, but interactive. +Admin-only. Per-provider credentials, test buttons, and the priority-ordering drag-and-drop UI. Equivalent to editing the `*_CLIENT_ID` / `*_API_KEY` env vars and `scan.priority` in `config.yml` but interactive. Full provider details in [Metadata Providers](metadata-providers.md). diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index 6f090427..a6a0fb95 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -49,11 +49,11 @@ Until email-based self-serve reset lands, admins set passwords manually: **Administration → Users → Edit → New password → Save.** -The next login on that account will use the new password, but existing sessions for that user remain valid until they expire. If that's a concern, revoke them explicitly by deleting all of the user's Client API Tokens. +The next login on that account will use the new password but existing sessions for that user remain valid until they expire. If that's a concern, revoke them explicitly by deleting all of the user's Client API Tokens. ### Self-serve password reset -Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA, but the UI path exists and will light up once email config is exposed.) +Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA but the UI path exists and will light up once email config is exposed.) ## OIDC @@ -86,7 +86,7 @@ Each user gets up to 25 active tokens, revokable from the same page. The API sid ## Kiosk mode -Turns every GET endpoint into unauthenticated read-only access: anyone reaching the instance can browse, but nobody can write, scan, upload, or manage users. +Turns every GET endpoint into unauthenticated read-only access: anyone reaching the instance can browse but nobody can write, scan, upload, or manage users. ```yaml environment: diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index f13f7fb4..6623a958 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -33,7 +33,7 @@ When you don't have shell access or you're uploading from a different machine. 3. Drag and drop the file, or click to browse. 4. RomM puts it in the correct `bios/{platform}/` directory on disk. -Editors and Admins can upload firmware, but Viewers can't (`firmware.write` scope required, see [Users & Roles](users-and-roles.md)). +Editors and Admins can upload firmware but Viewers can't (`firmware.write` scope required, see [Users & Roles](users-and-roles.md)). ## Platform-specific firmware diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md index 0ad17e1f..f400a5c4 100644 --- a/docs/administration/metadata-providers.md +++ b/docs/administration/metadata-providers.md @@ -74,7 +74,7 @@ Metadata, cover art, and screenshots. [Create an account](https://www.mobygames. !!! important "MobyGames API is paid" - Access to the MobyGames API is a [paid, non-commercial-licensed feature](https://www.mobygames.com/info/api/#non-commercial). RomM will continue to support it, but we recommend ScreenScraper as a free alternative. + Access to the MobyGames API is a [paid, non-commercial-licensed feature](https://www.mobygames.com/info/api/#non-commercial). RomM will continue to support it but we recommend ScreenScraper as a free alternative. ### SteamGridDB diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index 824176c1..b5fb5083 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -13,7 +13,7 @@ RomM's REST API accepts four authentication modes. Pick the one that matches you | **HTTP Basic** | Quick scripts, curl one-liners | `Authorization: Basic ` | | **OAuth2 Bearer** | Automation, CI, third-party apps | `Authorization: Bearer ` | | **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | -| **OIDC session** | Users who sign in via SSO | Same as session cookie, but issued after OIDC callback | +| **OIDC session** | Users who sign in via SSO | Same as session cookie but issued after OIDC callback | All of them resolve to the same scope model. See the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. @@ -130,7 +130,7 @@ A token that holds `users.write` also implicitly grants lesser scopes like `user | HTTP | Meaning | | ------------------ | ----------------------------------------------------------------------- | | `401 Unauthorized` | No credential, expired credential, bad credential. | -| `403 Forbidden` | Authenticated, but the identity lacks a required scope. | +| `403 Forbidden` | Authenticated but the identity lacks a required scope. | | `404 Not Found` | The resource doesn't exist, or, for privacy, the identity can't see it. | When debugging a 403, check: diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md index 784b91ab..e1321988 100644 --- a/docs/developers/api-reference.md +++ b/docs/developers/api-reference.md @@ -5,7 +5,7 @@ description: Catalogue of RomM's REST API. Authoritative interactive docs live o # API Reference -RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth. This page catalogues what's there, but every running RomM instance serves its own interactive browser with live "try it out" functionality. +RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth. This page catalogues what's there but every running RomM instance serves its own interactive browser with live "try it out" functionality. ## Interactive docs diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index d791b6b3..482c5e72 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -19,7 +19,7 @@ The project follows the [Contributor Covenant](https://github.com/rommapp/romm/b !!! warning "Required disclosure" - If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR, including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count, but anything more does. If PR responses are generated by an AI, disclose that too. + If you use **any kind of AI assistance** to contribute to RomM, disclose it in the PR, including the extent of assistance (docs only vs. full code generation). Trivial tab completion doesn't count but anything more does. If PR responses are generated by an AI, disclose that too. Example disclosures: @@ -27,7 +27,7 @@ Example disclosures: Or more granularly: -> I consulted ChatGPT to understand the codebase, but the solution was fully authored manually. +> I consulted ChatGPT to understand the codebase but the solution was fully authored manually. This isn't about gatekeeping AI contributions. It's about letting reviewers know how much scrutiny to apply. AI-assisted code often ranges from "fantastic" to "hallucinated nonsense", so we need to calibrate. diff --git a/docs/developers/i18n.md b/docs/developers/i18n.md index 46316565..817da725 100644 --- a/docs/developers/i18n.md +++ b/docs/developers/i18n.md @@ -81,7 +81,7 @@ Scenario: the language you want isn't in the dropdown yet. Match the source tone: -- **Conversational but not too casual.** "Let's scan your library" is fine, but "Yo, scan your roms dude" isn't. +- **Conversational but not too casual.** "Let's scan your library" is fine but "Yo, scan your roms dude" isn't. - **Active voice.** "Scan the library", not "The library is being scanned". - **Consistent formality.** Pick `tu`/`vous`, formal/informal `you`, etc., and stick with it across the locale. diff --git a/docs/developers/openapi.md b/docs/developers/openapi.md index 3e50000e..44aa3515 100644 --- a/docs/developers/openapi.md +++ b/docs/developers/openapi.md @@ -56,7 +56,7 @@ npx @openapitools/openapi-generator-cli generate \ -o ./romm-client-ts ``` -Generated clients handle auth, request shaping, and response parsing. Drop in, import, call. Quality varies by generator target, but Python and TypeScript are the best-tested. +Generated clients handle auth, request shaping, and response parsing. Drop in, import, call. Quality varies by generator target but Python and TypeScript are the best-tested. ### Tips diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index 5af999cf..8f758329 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -11,7 +11,7 @@ Tokens are **per-user** and **per-scope-subset**: a token can hold any subset of ## Why not just store a password? -- Passwords grant full access to the account, but tokens can be scope-narrowed. +- Passwords grant full access to the account but tokens can be scope-narrowed. - Tokens are one-click revocable without changing your password. - Tokens are safer to type (or paste) into a companion app's config file than a password. - Tokens can be bound to a single device via the pairing flow, avoiding typing them at all. @@ -145,7 +145,7 @@ DELETE /api/client-tokens/{id}/admin # revoke any user's token ## Scoping tokens properly -A token can only hold scopes the owning user _also_ holds. If the user is an Editor, their token can hold any Editor scope, but not `users.write` (which is Admin-only). +A token can only hold scopes the owning user _also_ holds. If the user is an Editor, their token can hold any Editor scope but not `users.write` (which is Admin-only). Useful narrow scope-sets: diff --git a/docs/ecosystem/fpkgi.md b/docs/ecosystem/fpkgi.md index 9f5d1b05..e5f00889 100644 --- a/docs/ecosystem/fpkgi.md +++ b/docs/ecosystem/fpkgi.md @@ -36,7 +36,7 @@ The feed returns JSON in the fpkgi-expected schema: titles, title IDs, content t ## Configuring fpkgi -Exact steps depend on the fpkgi version, but the gist: +Exact steps depend on the fpkgi version but the gist: 1. Put RomM's feed URL in fpkgi's config (usually a JSON file on the console, so check fpkgi's own docs). 2. Restart fpkgi. diff --git a/docs/ecosystem/grout.md b/docs/ecosystem/grout.md index 29492963..511fb89f 100644 --- a/docs/ecosystem/grout.md +++ b/docs/ecosystem/grout.md @@ -25,7 +25,7 @@ description: Official Linux handheld companion for muOS and NextUI, sync ROMs an - **[Argosy](argosy.md)** is Android-native. Good for Android handhelds (Retroid Pocket running Android, phones). - **Grout** is for non-Android Linux handhelds. muOS and NextUI are Linux, not Android. Argosy's APK won't install. -Same underlying protocol, but different client for different OS. +Same underlying protocol but different client for different OS. ## Installing @@ -87,7 +87,7 @@ Grout uses your Client API Token for all API calls. Token scopes: - `devices.read`, `devices.write`: to register as a device. - `firmware.read`: if you're syncing firmware too. -Scope the token narrowly when creating: default scopes are fine for most users, but an admin shouldn't hand Grout `users.write` just because the token-creation page offers it. +Scope the token narrowly when creating: default scopes are fine for most users but an admin shouldn't hand Grout `users.write` just because the token-creation page offers it. ## SSH sync (operator-side) diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md index 9d845873..03e37e7a 100644 --- a/docs/ecosystem/igir.md +++ b/docs/ecosystem/igir.md @@ -5,7 +5,7 @@ description: Clean up and normalise your ROM collection with Igir before importi # Igir Collection Manager -[Igir](https://igir.io/) is a zero-setup ROM collection manager: sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se, but more a pre-processing tool. Useful for cleaning up a library _before_ importing into RomM, so RomM's scans have a better-named, better-organised starting point. +[Igir](https://igir.io/) is a zero-setup ROM collection manager: sorts, filters, extracts, archives, patches, and reports on collections of any size. Not a RomM companion per se but more a pre-processing tool. Useful for cleaning up a library _before_ importing into RomM, so RomM's scans have a better-named, better-organised starting point. **This is not an official RomM app.** Igir is a separate community project. We document integration here because it's a common workflow and produces a RomM-compatible layout directly. diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md index d7331bb8..6b93e4bb 100644 --- a/docs/ecosystem/index.md +++ b/docs/ecosystem/index.md @@ -60,7 +60,7 @@ For developers building something new on top of RomM: ## External tooling -Not a RomM companion, but useful alongside: +Not a RomM companion but useful alongside: - **[Igir Collection Manager](igir.md)**: ROM sorting/verifying tool that cleans up library layout before importing into RomM. @@ -68,4 +68,4 @@ Not a RomM companion, but useful alongside: Built something RomM-adjacent? Open a PR on [rommapp/docs](https://github.com/rommapp/docs) adding it to [Community Apps](community-apps.md), or drop a link in the [Discord](https://discord.gg/romm) `#community-projects` channel. -We list active, maintained projects, with no gate on code quality, but we do flag abandoned projects so users know what's current. +We list active, maintained projects, with no gate on code quality but we do flag abandoned projects so users know what's current. diff --git a/docs/ecosystem/kekatsu.md b/docs/ecosystem/kekatsu.md index 0ea327b5..de7b09e2 100644 --- a/docs/ecosystem/kekatsu.md +++ b/docs/ecosystem/kekatsu.md @@ -29,7 +29,7 @@ http://192.168.1.100:3000/api/feeds/kekatsu/nds ## Configuring Kekatsu -Exact config steps depend on your Kekatsu build, but the shared concept is "point the app at this URL and it fetches the manifest". Consult Kekatsu's own docs for the current config-file location. +Exact config steps depend on your Kekatsu build but the shared concept is "point the app at this URL and it fetches the manifest". Consult Kekatsu's own docs for the current config-file location. ## File format diff --git a/docs/ecosystem/pkgj.md b/docs/ecosystem/pkgj.md index d918077a..c0cb766f 100644 --- a/docs/ecosystem/pkgj.md +++ b/docs/ecosystem/pkgj.md @@ -5,7 +5,7 @@ description: Install PS Vita and PSP games from your RomM library via pkgj homeb # pkgj -[pkgj](https://github.com/blastrock/pkgj) is PS Vita homebrew for installing `.pkg`-format games and DLC. Default config points at well-known community URLs, but you can point it at RomM's feed endpoints instead and install from your library over Wi-Fi. +[pkgj](https://github.com/blastrock/pkgj) is PS Vita homebrew for installing `.pkg`-format games and DLC. Default config points at well-known community URLs but you can point it at RomM's feed endpoints instead and install from your library over Wi-Fi. ## Prerequisites diff --git a/docs/ecosystem/webrcade.md b/docs/ecosystem/webrcade.md index 293881c8..371fd025 100644 --- a/docs/ecosystem/webrcade.md +++ b/docs/ecosystem/webrcade.md @@ -44,9 +44,9 @@ Why would you use WebRcade over RomM's built-in player? When to stay with RomM's player: -- **You want library management.** WebRcade is frontend-only, but RomM owns the metadata and scanning. +- **You want library management.** WebRcade is frontend-only but RomM owns the metadata and scanning. - **You want user accounts + collections + per-user progress.** WebRcade is single-user-ish. -- **You want [Netplay](../using/netplay.md).** RomM has it, but WebRcade doesn't. +- **You want [Netplay](../using/netplay.md).** RomM has it but WebRcade doesn't. Totally reasonable to run both: WebRcade as a launcher UI pointed at RomM for the library. diff --git a/docs/getting-started/concepts.md b/docs/getting-started/concepts.md index 97152e52..8cf8c71e 100644 --- a/docs/getting-started/concepts.md +++ b/docs/getting-started/concepts.md @@ -33,7 +33,7 @@ A named grouping of ROMs. Three flavours: - **Standard**: a hand-curated list. You pick what's in it. - **Smart**: rule-based and auto-populating. Define a query ("all SNES games rated 4+ stars") and RomM keeps it in sync. -- **Virtual**: auto-generated by RomM itself (by genre, developer, year, tag). Not user-editable, but you can toggle on/off in UI settings. +- **Virtual**: auto-generated by RomM itself (by genre, developer, year, tag). Not user-editable but you can toggle on/off in UI settings. ## Asset @@ -81,7 +81,7 @@ A separate `/console` UI optimised for gamepads and TV displays: spatial navigat ## Kiosk Mode -A server-side setting (`KIOSK_MODE=true`) that turns every read endpoint into unauthenticated access. Anonymous visitors can browse, but nobody can write. Useful for public demos and wall displays. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). +A server-side setting (`KIOSK_MODE=true`) that turns every read endpoint into unauthenticated access. Anonymous visitors can browse but nobody can write. Useful for public demos and wall displays. See [Authentication → Kiosk mode](../administration/authentication.md#kiosk-mode). ## EmulatorJS / Ruffle diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md index ec4b3dd2..af1b7c20 100644 --- a/docs/getting-started/first-scan.md +++ b/docs/getting-started/first-scan.md @@ -12,7 +12,7 @@ You've got RomM running ([Quick Start](quick-start.md)), your ROMs are laid out A fifteen-second check that saves hours: - **Library is mounted**: on the host, `ls /path/to/library` should show your `roms/` (or per-platform) folders. If it doesn't, the mount is wrong. -- **At least one metadata provider is configured**: RomM will scan without one, but every game comes back "unmatched" and you'll have nothing useful to look at. +- **At least one metadata provider is configured**: RomM will scan without one but every game comes back "unmatched" and you'll have nothing useful to look at. - **The Setup Wizard is done**: if RomM is still showing the wizard, finish that first. The first user becomes an admin. ## Run the scan diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index b5e2c410..f9226bdf 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -13,11 +13,11 @@ You'll need: - [Docker](https://docs.docker.com/get-docker/) and Docker Compose installed on the host. - Your ROM files organised in the expected [folder structure](folder-structure.md). -- API credentials for at least one [metadata provider](../administration/metadata-providers.md). Hasheous + IGDB + SteamGridDB + Retroachievements is the recommended pairing. RomM will run without any provider configured, but matching quality will suffer. +- API credentials for at least one [metadata provider](../administration/metadata-providers.md). Hasheous + IGDB + SteamGridDB + Retroachievements is the recommended pairing. RomM will run without any provider configured but matching quality will suffer. !!! warning "Metadata providers are recommended" - RomM works without a metadata API for basic use, but setup problems and companion-app integrations (e.g. Playnite) can fail without them. Setting up **IGDB**, **SteamGridDB**, and **Retroachievements** API keys before your first scan is strongly recommended. + RomM works without a metadata API for basic use but setup problems and companion-app integrations (e.g. Playnite) can fail without them. Setting up **IGDB**, **SteamGridDB**, and **Retroachievements** API keys before your first scan is strongly recommended. ## 1. Write your `docker-compose.yml` diff --git a/docs/install/backup-and-restore.md b/docs/install/backup-and-restore.md index 072e82e7..87b4dfbe 100644 --- a/docs/install/backup-and-restore.md +++ b/docs/install/backup-and-restore.md @@ -13,7 +13,7 @@ This page covers both routine backups and migrating RomM to a new host. | -------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical**: back this up nightly. | | **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical**: back this up nightly. | -| **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes, but small and painful to recreate. | +| **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes but small and painful to recreate. | | `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority, and can be re-downloaded on a rescan. Including it speeds up recovery. | | `/redis-data` | Task queue state | Low priority, in-flight tasks only, and lost tasks can be re-run. | | **`/romm/library`** | Your ROM files | Back this up **separately**. It's your source data and you should already have a backup strategy for it independent of RomM. | @@ -89,7 +89,7 @@ Offsite it however you already do (rclone to B2/S3, restic, borg, Proxmox Backup ### In place (oh no, something's broken) -Same steps, but skip step 1. Stop the stack, swap the DB dump back in, restart. Keep the dump you're about to overwrite: `mv current-broken.sql rollback.sql`. +Same steps but skip step 1. Stop the stack, swap the DB dump back in, restart. Keep the dump you're about to overwrite: `mv current-broken.sql rollback.sql`. ## Moving RomM to a new host @@ -140,7 +140,7 @@ sudo rsync -aHAX /var/lib/docker/volumes/romm_romm_resources/ new-host:/opt/rom # - /opt/romm/resources/_data:/romm/resources ``` -Works, but binds you to matching the old host's Docker layout. Use Option A unless you have a reason not to. +Works but binds you to matching the old host's Docker layout. Use Option A unless you have a reason not to. ## Verifying a backup is actually restorable diff --git a/docs/install/databases.md b/docs/install/databases.md index 8381d84b..1a8d88dd 100644 --- a/docs/install/databases.md +++ b/docs/install/databases.md @@ -50,7 +50,7 @@ services: ## MySQL -Identical compose to MariaDB, but swap the image and the healthcheck: +Identical compose to MariaDB but swap the image and the healthcheck: ```yaml services: @@ -130,7 +130,7 @@ Exact keys depend on the driver. See SQLAlchemy / the driver's docs. - **Sticking with defaults?** MariaDB. That's what the reference compose uses and what the team tests against. - **Already run Postgres?** Postgres. No reason to add a second DB engine. -- **Single-user laptop demo?** SQLite is fine, but upgrade before adding anyone else. +- **Single-user laptop demo?** SQLite is fine but upgrade before adding anyone else. - **External managed DB?** Any of MariaDB / MySQL / Postgres. Point `DB_HOST` at it and configure TLS via `DB_QUERY_JSON`. Don't switch DB drivers on a running install without a plan. Migrating the data requires a dump + reload, covered in [Backup & Restore](backup-and-restore.md). diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index c02d30d8..5057b438 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -10,7 +10,7 @@ The canonical way to run RomM is with Docker Compose, and this page describes th The RomM stack has three parts: 1. **`romm`**: the application container with Valkey embedded. -2. **A database**: MariaDB by default, but MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. +2. **A database**: MariaDB by default but MySQL and PostgreSQL are also supported. See [Databases](databases.md) for details and driver-specific notes. ## Reference `docker-compose.yml` @@ -80,4 +80,4 @@ docker compose pull docker compose up -d ``` -Alembic migrations run automatically on startup, but read the [release notes](../releases/index.md) before every major-version bump. The 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). +Alembic migrations run automatically on startup but read the [release notes](../releases/index.md) before every major-version bump. The 4.x → 5.0 jump in particular has breaking changes, covered in [Upgrading to 5.0](../releases/upgrading-to-5.0.md). diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index 74786191..e01cc73a 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -311,7 +311,7 @@ spec: ## Scaling notes - **One RomM replica** is the simple path. The scan runs as a single worker, which prefers a single replica. -- **Multiple replicas** work, but you need RWX for `/romm/assets` and `/romm/resources` and an external Redis (set `REDIS_HOST` to a shared service). See [Redis or Valkey](redis-or-valkey.md). +- **Multiple replicas** work but you need RWX for `/romm/assets` and `/romm/resources` and an external Redis (set `REDIS_HOST` to a shared service). See [Redis or Valkey](redis-or-valkey.md). - **No HPA** (horizontal pod autoscaler) on RomM: CPU spikes during scans are normal and not a scaling signal. ## Updating diff --git a/docs/install/unraid.md b/docs/install/unraid.md index f5acb719..8be11b85 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -119,7 +119,7 @@ Community-made, still relevant for general Unraid/RomM debugging even if specifi [![RomM on Unraid, DemonWarriorTech](https://img.youtube.com/vi/Oo5obHNy2iw/0.jpg)](https://www.youtube.com/watch?v=Oo5obHNy2iw) -[AlienTech42](https://www.youtube.com/@AlienTech42): [older install + debugging walkthrough](https://www.youtube.com/watch?v=ls5YcsFdwLQ). Install steps are out of date, but the general-setup and debugging portions still hold. +[AlienTech42](https://www.youtube.com/@AlienTech42): [older install + debugging walkthrough](https://www.youtube.com/watch?v=ls5YcsFdwLQ). Install steps are out of date but the general-setup and debugging portions still hold. [![RomM on Unraid, AlienTech42](https://img.youtube.com/vi/ls5YcsFdwLQ/0.jpg)](https://www.youtube.com/watch?v=ls5YcsFdwLQ) diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index d6f03dfd..65ca8685 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -84,7 +84,7 @@ Same mechanic: just use a filename matching an existing platform's slug and your - **Metadata provider coverage.** Providers are IGDB-slug-driven. A genuinely unknown platform won't have IGDB / ScreenScraper / MobyGames data. - **EmulatorJS support.** The platform has to match a known EmulatorJS core. See [Supported Platforms → EmulatorJS column](supported-platforms.md). -- **RetroAchievements.** Hash-based, but restricted to RA-supported platforms. +- **RetroAchievements.** Hash-based but restricted to RA-supported platforms. For the niche platform case, you'll likely rely on filename-only matching (no provider) and browsing the library like a straight file system. diff --git a/docs/platforms/emulatorjs-config.md b/docs/platforms/emulatorjs-config.md index 0cca0d7b..1c4e54b1 100644 --- a/docs/platforms/emulatorjs-config.md +++ b/docs/platforms/emulatorjs-config.md @@ -16,7 +16,7 @@ environment: - ENABLE_EMULATORJS=true # default ``` -Set to `false` to disable EmulatorJS entirely: useful on the slim image or when running headless with companion apps only. The Play button is hidden, but Ruffle still works independently. See [Image Variants](../install/image-variants.md). +Set to `false` to disable EmulatorJS entirely: useful on the slim image or when running headless with companion apps only. The Play button is hidden but Ruffle still works independently. See [Image Variants](../install/image-variants.md). ## Debug mode diff --git a/docs/platforms/firmware-by-platform.md b/docs/platforms/firmware-by-platform.md index 1f7fa7d8..792edbde 100644 --- a/docs/platforms/firmware-by-platform.md +++ b/docs/platforms/firmware-by-platform.md @@ -18,7 +18,7 @@ This page lists the firmware RomM's EmulatorJS cores need for each platform. For | **PlayStation (PSX)** | `ps` | `scph1001.bin` (US), `scph5501.bin` (US), `scph5502.bin` (EU), `scph7502.bin` (JP) | At least one region BIOS required. | | **Saturn** | `saturn` | `saturn_bios.bin`, optional `mpr-17933.bin` (EU) | Bundle both in a zip. | | **Sega CD / Mega CD** | `segacd` | `bios_CD_U.bin` (US), `bios_CD_E.bin` (EU), `bios_CD_J.bin` (JP) | Region depends on your games. | -| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional. Games run without it, but accurate emulation needs it. | +| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional. Games run without it but accurate emulation needs it. | | **Nintendo DS** | `nds` | `bios7.bin`, `bios9.bin`, `firmware.bin` | All three are needed. | | **Atari Lynx** | `lynx` | `lynxboot.img` | | | **Neo Geo Pocket / Color** | `ngp` / `ngpc` | `neopop.rom` | Bundle as zip. | diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md index 2b00955f..3b426ab6 100644 --- a/docs/platforms/ms-dos.md +++ b/docs/platforms/ms-dos.md @@ -36,7 +36,7 @@ dir # find the .EXE filename.exe # launch ``` -Works for homebrew and shareware, but retail games usually fail here because they want a CD mounted somewhere. +Works for homebrew and shareware but retail games usually fail here because they want a CD mounted somewhere. ## The automatic way (`.conf` autoload) diff --git a/docs/platforms/ruffle-config.md b/docs/platforms/ruffle-config.md index e114a754..6fa19e68 100644 --- a/docs/platforms/ruffle-config.md +++ b/docs/platforms/ruffle-config.md @@ -81,7 +81,7 @@ Ruffle is bundled in the full RomM image. Updates to Ruffle land when RomM updat ## Not in 5.0 yet -- **Per-game config overrides.** Ruffle supports some game-specific options upstream, but RomM doesn't surface them yet. +- **Per-game config overrides.** Ruffle supports some game-specific options upstream but RomM doesn't surface them yet. - **Networking.** Flash games that hit remote servers typically fail (those servers are dead). No proxy / emulated backend for networked Flash games. - **Control remapping.** Straight passthrough only. diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index fb6dd627..01716f18 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -56,7 +56,7 @@ For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../a ## When env vars are read - **Startup:** most vars are consumed once at container start, so changes need a `docker compose up -d` to apply. -- **Per-request:** a handful of feature toggles (`KIOSK_MODE`, `DISABLE_USERPASS_LOGIN`) are re-checked per request, but a restart is safest to avoid partial-state caching. +- **Per-request:** a handful of feature toggles (`KIOSK_MODE`, `DISABLE_USERPASS_LOGIN`) are re-checked per request but a restart is safest to avoid partial-state caching. - **Never at runtime:** there's no reload-config endpoint. ## Full reference diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md index e3a60474..2f0246ae 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -33,7 +33,7 @@ For the pedagogical version with reasoning and examples, see [Core Concepts](../ **Device**: a registered endpoint that syncs with RomM. Tracked via [Device Sync Protocol](../ecosystem/device-sync-protocol.md). -**Editor**: mid-tier user role. Edit content (ROMs, platforms, collections), upload, but no user management. See [Users & Roles](../administration/users-and-roles.md). +**Editor**: mid-tier user role. Edit content (ROMs, platforms, collections), upload but no user management. See [Users & Roles](../administration/users-and-roles.md). **EmulatorJS**: the in-browser retro emulator RomM uses for most platforms. [In-Browser Play](../using/in-browser-play.md). diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index 0dcbc6ac..23acaf09 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -48,7 +48,7 @@ Most of the RomM web UI: every page under `/`. Authenticated via browser cookie. ### Token-authenticated (bearer) -Every API endpoint under `/api/...` that requires a specific scope. Session cookies also work here, but token auth is for scripts / companion apps. +Every API endpoint under `/api/...` that requires a specific scope. Session cookies also work here but token auth is for scripts / companion apps. ### Download endpoints (optionally auth-off) diff --git a/docs/releases/index.md b/docs/releases/index.md index fb325a77..1aa23f5f 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -14,7 +14,7 @@ RomM's current stable is [`5.0.0`](changelog.md). Read [Upgrading to 5.0](upgrad RomM follows **SemVer** for breaking changes and **CalVer-ish** for cadence: major versions are planned milestones, not scheduled. - **Patch releases** (`5.0.1`, `5.0.2`): bug fixes only. Safe to pull automatically. No migration. -- **Minor releases** (`5.1.0`, `5.2.0`): additive features. Schema may migrate automatically via Alembic, but no action required beyond reading the release notes. +- **Minor releases** (`5.1.0`, `5.2.0`): additive features. Schema may migrate automatically via Alembic but no action required beyond reading the release notes. - **Major releases** (`5.0.0`, `6.0.0`): breaking changes. Read the migration guide before upgrading. ## Image tags and what to pin @@ -53,7 +53,7 @@ Older frozen docs are retained for 12 months after the major's support window en ## Migration guides - **[Upgrading to 5.0](upgrading-to-5.0.md):** 4.x → 5.0. Required reading. -- **[Upgrading to 3.0](upgrading-to-3.0.md):** 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical, but kept for 2.x migrators. +- **[Upgrading to 3.0](upgrading-to-3.0.md):** 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical but kept for 2.x migrators. ## Where releases are announced diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index 387b403e..7f0e60c7 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -90,7 +90,7 @@ See [OIDC with Zitadel → Enable claims](../administration/oidc/zitadel.md) for ### Authentik 2025.10: login succeeds but RomM rejects the user -Authentik 2025.10 changed the default `email_verified` claim from `true` to `false`, but RomM requires a verified email so the claim must arrive as `true`. +Authentik 2025.10 changed the default `email_verified` claim from `true` to `false` but RomM requires a verified email so the claim must arrive as `true`. Fix: add the property mapping documented in [OIDC with Authentik → Create a property mapping](../administration/oidc/authentik.md#2-create-a-property-mapping-authentik-202510). diff --git a/docs/troubleshooting/in-browser-play.md b/docs/troubleshooting/in-browser-play.md index 59c859c4..e4d6b5ac 100644 --- a/docs/troubleshooting/in-browser-play.md +++ b/docs/troubleshooting/in-browser-play.md @@ -17,7 +17,7 @@ Checks in order: ## Black screen, no audio -EmulatorJS loaded, but the game is dead. +EmulatorJS loaded but the game is dead. - **Core incompatibility.** Some cores have issues with specific ROMs. Try a different core via in-game Menu → **Core**. - **ROM dump is bad.** A broken dump produces a black screen on most emulators. Try a known-good No-Intro / Redump version. @@ -69,7 +69,7 @@ Rebind via Profile → User Interface (the operator-side overrides live in [`emu ## DOS games fail to boot - **autorun.bat issues**: turn on [`emulatorjs.disable_batch_bootup: true`](../reference/configuration-file.md#emulatorjsdisable_batch_bootup) in `config.yml`. -- **Sound card config wrong**: dosbox-pure tries to auto-detect, but may need manual tweaking. In-game Menu → **Settings** → set Sound Blaster port. +- **Sound card config wrong**: dosbox-pure tries to auto-detect but may need manual tweaking. In-game Menu → **Settings** → set Sound Blaster port. - **Game needs specific CPU speed**: some DOS games are CPU-bound. Slow down via dosbox-pure settings. See the [MS-DOS platform guide](../platforms/ms-dos.md) for deeper DOS-specific notes. diff --git a/docs/troubleshooting/netplay.md b/docs/troubleshooting/netplay.md index 6530b78e..700b75ee 100644 --- a/docs/troubleshooting/netplay.md +++ b/docs/troubleshooting/netplay.md @@ -21,7 +21,7 @@ Full config: [Configuration File → `emulatorjs.netplay`](../reference/configur ## Can't see the room -You created a room as host, but other players don't see it. +You created a room as host but other players don't see it. - **They need RomM accounts on your instance.** Netplay doesn't federate. A user with no account or on a different RomM can't see or join. - **WebSocket connection is broken.** Open devtools → Network → WS tab. If Socket.IO is disconnecting, see [Authentication Troubleshooting → WebSockets](authentication.md#400-bad-request-on-the-websocket-endpoint). @@ -47,7 +47,7 @@ Different players' screens diverge over time: you do a move, they don't see it. - **Browser tab backgrounded.** Browsers throttle. Keep both tabs foregrounded. - **Bandwidth starvation.** Video stream is dropping frames, inputs are queueing. Reduce resolution or lower the emulator frame rate. -- **Core-specific bug.** Not all cores handle Netplay cleanly. `snes9x` and `genesis_plus_gx` are most reliable, but some 64-bit era cores have known desync bugs. +- **Core-specific bug.** Not all cores handle Netplay cleanly. `snes9x` and `genesis_plus_gx` are most reliable but some 64-bit era cores have known desync bugs. ## Audio crackle / cuts out on the client side diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md index e17f1b0b..ffc3ee94 100644 --- a/docs/troubleshooting/sync.md +++ b/docs/troubleshooting/sync.md @@ -80,7 +80,7 @@ OpenSSH refuses to use keys with loose permissions. On the host: chmod 600 /path/to/romm-sync-key ``` -And verify the Docker bind mount isn't forcing different perms (ro is fine, but mode-reset-via-mount-option is the usual culprit). +And verify the Docker bind mount isn't forcing different perms (ro is fine but mode-reset-via-mount-option is the usual culprit). ### Sync task doesn't run diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index 5a7811fa..c6ed2a4b 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -20,7 +20,7 @@ Editable from the profile page. Changing your email: ### Password -For local accounts: **Current password** + **New password** (twice). RomM uses bcrypt: no plaintext storage, no password recovery by the admin (admins reset by setting a new password, but they can't see your old one). +For local accounts: **Current password** + **New password** (twice). RomM uses bcrypt: no plaintext storage, no password recovery by the admin (admins reset by setting a new password but they can't see your old one). OIDC users don't have local passwords, because authentication is via the IdP. Password fields are hidden on the profile page. @@ -124,7 +124,7 @@ When deleted: - Your profile is removed. - Your saves, states, screenshots, personal ROM data (ratings, notes, play sessions) are removed. -- Your **public collections** stay (they belong to the community), but show as orphaned. +- Your **public collections** stay (they belong to the community) but show as orphaned. - Your **private collections** are deleted. - Your **Client API Tokens** are revoked. diff --git a/docs/using/collections.md b/docs/using/collections.md index 9e767b1c..c5067ad3 100644 --- a/docs/using/collections.md +++ b/docs/using/collections.md @@ -60,7 +60,7 @@ Change: ## Sharing -If the collection is **Public**, any other user on the same RomM instance sees it in their Collections drawer. They can browse, play, download, but can't add or remove ROMs. Only the owner edits. +If the collection is **Public**, any other user on the same RomM instance sees it in their Collections drawer. They can browse, play, download but can't add or remove ROMs. Only the owner edits. For cross-instance sharing, generate a link via the collection drawer → **Copy link**. Anyone with access to your RomM instance and the link can open it directly. diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md index f2decc62..d4dcdd36 100644 --- a/docs/using/console-mode.md +++ b/docs/using/console-mode.md @@ -61,7 +61,7 @@ If you're browsing `/console` without a gamepad for some reason: ### Touch -Console Mode is gamepad-first, but touch works on handhelds without native gamepad input: tap to activate, swipe to scroll. +Console Mode is gamepad-first but touch works on handhelds without native gamepad input: tap to activate, swipe to scroll. ## What's different from the main UI @@ -141,14 +141,14 @@ For multi-disc games, Console Mode asks which disc to boot before launching. For - **Admin features aren't available**: if you're the admin, flip back to the main UI for scans / user management. - **Some metadata tabs collapse**: the main UI's "Related Games" + "Additional Content" tabs may be merged on the smaller Console detail view. -- **Mobile browsers with no gamepad**: touch works, but the UX is designed for gamepads, not fingers. Use the main UI or the Argosy mobile app ([Ecosystem](../ecosystem/argosy.md)). +- **Mobile browsers with no gamepad**: touch works but the UX is designed for gamepads, not fingers. Use the main UI or the Argosy mobile app ([Ecosystem](../ecosystem/argosy.md)). ## Handheld-specific notes Running on muOS / Batocera / Knulli / a Steam Deck? Consider: - **[Grout](../ecosystem/grout.md)**: official handheld companion that syncs saves/states to/from the device. -- **[Argosy Launcher](../ecosystem/argosy.md)**: Android handhelds that can run a browser, but want a native-feeling app. +- **[Argosy Launcher](../ecosystem/argosy.md)**: Android handhelds that can run a browser but want a native-feeling app. Both use [Client API Tokens](../ecosystem/client-api-tokens.md) for auth. diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md index 9a1a3bc0..5f66db92 100644 --- a/docs/using/in-browser-play.md +++ b/docs/using/in-browser-play.md @@ -73,7 +73,7 @@ If the platform doesn't support in-browser play, Play is replaced with Download. ### Controls -EmulatorJS maps keyboard and gamepad automatically. Defaults are core-specific, but approximate the original console layout. Rebind in-game via the **Menu** button during play. +EmulatorJS maps keyboard and gamepad automatically. Defaults are core-specific but approximate the original console layout. Rebind in-game via the **Menu** button during play. Operator-level default overrides live in `config.yml`. See [Configuration File → `emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols). @@ -116,7 +116,7 @@ Profile → User Interface → **Fullscreen on launch**: always enters fullscree ### Per-core settings -In-game Menu → **Settings**: core-specific knobs (vsync, region, sound quality). Your tweaks persist per-core per-user, but operator defaults in [`config.yml`](../reference/configuration-file.md#emulatorjssettings) set the initial values. +In-game Menu → **Settings**: core-specific knobs (vsync, region, sound quality). Your tweaks persist per-core per-user but operator defaults in [`config.yml`](../reference/configuration-file.md#emulatorjssettings) set the initial values. ### Netplay diff --git a/docs/using/languages.md b/docs/using/languages.md index 2b2e63ab..73fb4eac 100644 --- a/docs/using/languages.md +++ b/docs/using/languages.md @@ -33,7 +33,7 @@ Two more ship as work-in-progress with partial coverage. Check the language drop ## What gets translated -- All UI strings: menus, buttons, labels, tooltips, dialogs. +- All UI strings: menus buttons, labels, tooltips, dialogs. - Error messages and notifications. - Help text and inline instructions. diff --git a/docs/using/library.md b/docs/using/library.md index f27d19d0..83c96ca1 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -123,7 +123,7 @@ Two side buttons: ## Collection view -Like the platform view, but scoped to one collection. Same filters and grid/list toggle. The side drawer shows collection metadata (name, owner, visibility, game count) and, for collections you own, Edit and Delete buttons. +Like the platform view but scoped to one collection. Same filters and grid/list toggle. The side drawer shows collection metadata (name, owner, visibility, game count) and, for collections you own, Edit and Delete buttons. Three collection types: see [Collections](collections.md), [Smart Collections](smart-collections.md), [Virtual Collections](virtual-collections.md). diff --git a/docs/using/pwa.md b/docs/using/pwa.md index 7c5cb453..8ccae03c 100644 --- a/docs/using/pwa.md +++ b/docs/using/pwa.md @@ -32,7 +32,7 @@ Similar: look for "Add to Home screen" in the share / menu. ## Install on iOS -iOS Safari is slightly different and has no true PWA parity (some APIs are missing), but the basic install flow works: +iOS Safari is slightly different and has no true PWA parity (some APIs are missing) but the basic install flow works: 1. Open your RomM URL in **Safari** (not Chrome, because on iOS, Chrome uses Safari's engine but doesn't expose the install flow). 2. Tap the **Share** button (square with arrow). @@ -94,7 +94,7 @@ If a stale shell is causing issues (e.g. seeing old UI after an upgrade), force- - **Requires HTTPS**: PWAs don't install from plain-HTTP hosts. Make sure your RomM is behind a reverse proxy with TLS. See [Reverse Proxy](../install/reverse-proxy.md). - **Icons**: RomM ships 192×192 and 512×512 manifest icons. Some devices pick a mid-size fallback that looks slightly blurry. Known limitation, we'll expand the icon set over time. - **No push notifications yet**: the PWA manifest doesn't register a notification handler in 5.0. Scan completion, task failures, etc. don't notify you. -- **Offline mode is partial**: opening the installed PWA offline shows the shell, but you can't actually browse the library or play anything without the server reachable. +- **Offline mode is partial**: opening the installed PWA offline shows the shell but you can't actually browse the library or play anything without the server reachable. ## Use with Console Mode diff --git a/docs/using/retroachievements.md b/docs/using/retroachievements.md index 342950e9..7b6f54c3 100644 --- a/docs/using/retroachievements.md +++ b/docs/using/retroachievements.md @@ -23,7 +23,7 @@ When RomM is wired up to RetroAchievements, each user's progression (achievement - **API Key**: the key you just copied. 4. **Sync now**: RomM pulls your progression data and populates the Achievements tab on matched games. -From this point on, RomM auto-syncs every user's progression via the nightly RetroAchievements Sync scheduled task. See [Scheduled Tasks](../administration/scheduled-tasks.md). No manual sync needed, but you can force one from the Profile page whenever. +From this point on, RomM auto-syncs every user's progression via the nightly RetroAchievements Sync scheduled task. See [Scheduled Tasks](../administration/scheduled-tasks.md). No manual sync needed but you can force one from the Profile page whenever. ## Where it shows up @@ -57,7 +57,7 @@ RetroAchievements distinguishes two play modes: - **Softcore**: save states allowed. Achievements still count. - **Hardcore**: no save states. More points per achievement, flagged separately. -RomM doesn't enforce hardcore. You toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement, but marked as softcore. +RomM doesn't enforce hardcore. You toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement but marked as softcore. If you care about hardcore, use the in-game save feature instead of save states. See [Saves & States → RetroAchievements and states](saves-and-states.md#retroachievements-and-states). diff --git a/docs/using/rom-patcher.md b/docs/using/rom-patcher.md index 16c41af7..6723139f 100644 --- a/docs/using/rom-patcher.md +++ b/docs/using/rom-patcher.md @@ -73,7 +73,7 @@ No-intro patches, region-free patches. Apply, download, and verify with the Hash ## Limits -- **Browser memory**: huge ROMs (PSX / Saturn / PSP ISOs, > 1 GB) can struggle in browsers with low memory limits. Safari is the most restrictive, but Firefox / Chrome handle bigger files. +- **Browser memory**: huge ROMs (PSX / Saturn / PSP ISOs, > 1 GB) can struggle in browsers with low memory limits. Safari is the most restrictive but Firefox / Chrome handle bigger files. - **Multi-file ROMs**: the Patcher operates on a single file. For multi-disc games, patch each ISO separately. - **Encrypted ROMs**: if the patch was authored against a decrypted ROM (e.g. DS `.nds` vs raw cartridge), your source has to match. - **Save format changes**: patches that alter save-data layout will invalidate existing save files. Back them up before applying. diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md index 482bc551..d5e1a86b 100644 --- a/docs/using/saves-and-states.md +++ b/docs/using/saves-and-states.md @@ -80,7 +80,7 @@ From the end-user side: once your device is paired and sync is running, saves ma ### Saves -Save files are usually format-interchangeable across cores for the same platform, but not always. +Save files are usually format-interchangeable across cores for the same platform but not always. | Platform | Format | Usually-compatible | | -------------------- | ---------------------- | -------------------------------------------------- | @@ -88,7 +88,7 @@ Save files are usually format-interchangeable across cores for the same platform | SNES | `.srm` | Yes, across SNES9x / bsnes | | Genesis / Mega Drive | `.srm` | Yes | | Game Boy / GBC / GBA | `.sav` | Yes, across Gambatte / mGBA | -| N64 | `.srm`, `.eep`, `.fla` | Yes, but per-type: the right file must be uploaded | +| N64 | `.srm`, `.eep`, `.fla` | Yes but per-type: the right file must be uploaded | | PSX | `.srm` (memory card) | Yes, across Mednafen / PCSX cores | | Saturn / Dreamcast | Varies | Check core docs | @@ -102,7 +102,7 @@ Practical advice: stick to one core per platform if you use states heavily, or u ## RetroAchievements and states -If you use [RetroAchievements](retroachievements.md) in hardcore mode, loading a save state **disables achievement tracking** for that session. RomM doesn't enforce this, but the RA server will stop crediting achievements. Use save files (the in-game save) instead of states if you care. +If you use [RetroAchievements](retroachievements.md) in hardcore mode, loading a save state **disables achievement tracking** for that session. RomM doesn't enforce this but the RA server will stop crediting achievements. Use save files (the in-game save) instead of states if you care. ## Screenshots with states diff --git a/docs/using/smart-collections.md b/docs/using/smart-collections.md index 6a08d531..6da86d59 100644 --- a/docs/using/smart-collections.md +++ b/docs/using/smart-collections.md @@ -125,7 +125,7 @@ Smart collections refresh: - **Live**: when you add, remove, rate, or edit a ROM, RomM re-evaluates rules instantly. - **On scan**: after every scan, rules are re-evaluated against the new state. -- **No manual refresh needed**, but admins can trigger a full re-evaluation via **Administration → Tasks → Refresh Smart Collections** if something looks stale. +- **No manual refresh needed** but admins can trigger a full re-evaluation via **Administration → Tasks → Refresh Smart Collections** if something looks stale. ## Editing a rule @@ -138,7 +138,7 @@ Same as standard collections: removes the definition. ROMs stay in the library. ## Limitations - **No nested smart collections**: a smart collection can't reference another collection as a source. Compose rules directly. -- **Performance**: very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible, but mentioned here for completeness. +- **Performance**: very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible but mentioned here for completeness. - **Timezone**: "Release Year" uses UTC, not the user's timezone. Edge-case edge-of-year games might fall on the "wrong" side. ## API diff --git a/docs/using/uploads.md b/docs/using/uploads.md index 34077f60..0e5119c8 100644 --- a/docs/using/uploads.md +++ b/docs/using/uploads.md @@ -48,7 +48,7 @@ To upload a multi-file game (multi-disc, game + DLC): ### After upload -Uploaded ROMs are immediately visible in the platform view, but aren't **matched** to metadata until the next scan. Two options: +Uploaded ROMs are immediately visible in the platform view but aren't **matched** to metadata until the next scan. Two options: - **Run a Quick scan**: picks up new files only. - **Match manually**: game detail → Match → search for the title. From c5745b646d5c4d80823d6fc8134c68eca58c2cb4 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 16:14:10 -0400 Subject: [PATCH 041/121] finish oidc --- docs/Navigation.md | 1 + docs/administration/authentication.md | 4 -- .../invitations-and-registration.md | 2 +- docs/administration/oidc/authelia.md | 16 +++----- docs/administration/oidc/authentik.md | 14 +++---- docs/administration/oidc/index.md | 37 +++++++++---------- docs/administration/oidc/keycloak.md | 31 ++++++++-------- docs/administration/oidc/pocketid.md | 15 ++++---- docs/administration/oidc/zitadel.md | 31 ++++++---------- docs/administration/server-stats.md | 6 --- docs/administration/users-and-roles.md | 4 +- 11 files changed, 67 insertions(+), 94 deletions(-) diff --git a/docs/Navigation.md b/docs/Navigation.md index 0c9a1d57..4af7480e 100644 --- a/docs/Navigation.md +++ b/docs/Navigation.md @@ -119,6 +119,7 @@ search: - Release Notes & Migration - [Overview](releases/index.md) - [Upgrading to 5.0](releases/upgrading-to-5.0.md) + - [Upgrading to 4.0](releases/upgrading-to-4.0.md) - [Upgrading to 3.0](releases/upgrading-to-3.0.md) - [Changelog](releases/changelog.md) - About diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index a6a0fb95..428ab5f6 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -51,10 +51,6 @@ Until email-based self-serve reset lands, admins set passwords manually: The next login on that account will use the new password but existing sessions for that user remain valid until they expire. If that's a concern, revoke them explicitly by deleting all of the user's Client API Tokens. -### Self-serve password reset - -Users can click "Forgot password?" on the login page if you've configured an SMTP transport. (Not part of 5.0 GA but the UI path exists and will light up once email config is exposed.) - ## OIDC See [OIDC Setup](oidc/index.md) for the full walkthrough. One-liner config sketch: diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md index 20c917a1..9ce0e51d 100644 --- a/docs/administration/invitations-and-registration.md +++ b/docs/administration/invitations-and-registration.md @@ -78,6 +78,6 @@ Changing a user's role afterwards is a normal admin action. See [Users & Roles]( ## Password reset -If you've configured an email transport (future work, not part of 5.0 GA), users can self-serve a password reset via **Forgot password?** on the login page. Until then, admins reset passwords manually: **Administration → Users → Edit → New password**. +Admins can reset passwords manually in **Administration → Users → Edit → New password**. A temporary password will be printed to the container's logs. See [Authentication](authentication.md) for the session and token side of the auth model. diff --git a/docs/administration/oidc/authelia.md b/docs/administration/oidc/authelia.md index 5f694101..08417e97 100644 --- a/docs/administration/oidc/authelia.md +++ b/docs/administration/oidc/authelia.md @@ -1,17 +1,15 @@ --- title: OIDC with Authelia -description: Wire RomM's SSO to Authelia: claims policy, client registration, RomM env vars. +description: Wire up SSO to Authelia --- # OIDC with Authelia -[Authelia](https://www.authelia.com/) is a lightweight open-source authentication and authorisation server with two-factor auth and SSO. Good fit for homelabs that already proxy through a reverse proxy. - -Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. +[Authelia](https://www.authelia.com/) is a lightweight open-source authentication and authorisation server with two-factor auth and SSO. Before starting, read the [OIDC Setup overview](index.md), as it covers the RomM-side settings common to every provider. ## 1. Prerequisites -Authelia installed and running, with its OIDC provider enabled. Upstream guides: +Authelia installed and running, with its OIDC provider enabled: - [Authelia getting started](https://www.authelia.com/integration/prologue/get-started/) - [OIDC provider configuration](https://www.authelia.com/configuration/identity-providers/openid-connect/provider/) @@ -63,7 +61,7 @@ identity_providers: token_endpoint_auth_method: "client_secret_basic" ``` -Generating IDs and secrets: see [Authelia's FAQ](https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret). Full client schema: [Authelia clients reference](https://www.authelia.com/configuration/identity-providers/openid-connect/clients/). +To generate IDs and secrets, see [Authelia's FAQ](https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret). The full client schema is available in the [Authelia clients reference](https://www.authelia.com/configuration/identity-providers/openid-connect/clients/). ## 4. Configure RomM @@ -80,9 +78,7 @@ environment: - ROMM_BASE_URL=https://romm.example.com ``` -`OIDC_REDIRECT_URI` must match what you put in `redirect_uris` exactly: scheme, host, path, no trailing slash. - -For role mapping from Authelia groups, see [OIDC Setup → Role mapping](index.md#role-mapping-50). +`OIDC_REDIRECT_URI` must match what you put in `redirect_uris` exactly (scheme, host, path, no trailing slash). For role mapping from Authelia groups, see [OIDC Setup → Role mapping](index.md#role-mapping). ## 5. Set your email on RomM @@ -92,7 +88,7 @@ In RomM → **Profile** → set your email to exactly the same address Authelia ## 6. Test -Restart RomM and open `/login`. There should be a **Login with OIDC** button alongside the username/password form. Click it → you're bounced to Authelia → authenticate → you're bounced back and signed into RomM. +Restart RomM, navigate to `/login` and click the **Login with OIDC** button. You're redirected to Authelia → authenticate → bounced back and signed into RomM! ![Login with OIDC](../../resources/authelia/2-romm-login.png) diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md index 8d881648..2458a390 100644 --- a/docs/administration/oidc/authentik.md +++ b/docs/administration/oidc/authentik.md @@ -1,17 +1,15 @@ --- title: OIDC with Authentik -description: Wire RomM's SSO to Authentik: property mapping for email_verified, provider, application, RomM env vars. +description: Wire up SSO to Authentik --- # OIDC with Authentik -[Authentik](https://goauthentik.io/) is a full-featured open-source IdP with MFA, flows, and a sizeable audit/admin surface. Good fit for users who want more than Authelia offers. - -Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. +[Authentik](https://goauthentik.io/) is a full-featured open-source IdP with MFA, flows, and a sizeable audit/admin surface. Before starting, read the [OIDC Setup overview](index.md), as it covers the RomM-side settings common to every provider. ## 1. Prerequisites -Authentik installed and running. Upstream: [Docker Compose install guide](https://docs.goauthentik.io/docs/install-config/install/docker-compose). +Authentik installed and running via their [install guide](https://docs.goauthentik.io/docs/install-config/install/docker-compose). Log in as admin and open **Admin Interface**. @@ -89,9 +87,9 @@ environment: - ROMM_BASE_URL=https://romm.example.com ``` -Note `OIDC_SERVER_APPLICATION_URL` points at the per-application URL (`/application/o/`), not the Authentik root. +Note that `OIDC_SERVER_APPLICATION_URL` points at the per-application URL (`/application/o/`), not the Authentik root. -For role mapping from Authentik groups, see [OIDC Setup → Role mapping](index.md#role-mapping-50). +For role mapping from Authentik groups, see [OIDC Setup → Role mapping](index.md#role-mapping). ## 6. Set your email on RomM @@ -101,7 +99,7 @@ In RomM → **Profile** → set your email to exactly the same address Authentik ## 7. Test -Restart RomM and open `/login`. Click the **Login with OIDC** button. You're redirected to Authentik, authenticate, and come back signed into RomM. +Restart RomM, navigate to `/login` and click the **Login with OIDC** button. You're redirected to Authentik → authenticate → bounced back and signed into RomM! ![Login with OIDC](../../resources/authentik/8-romm-login.png) diff --git a/docs/administration/oidc/index.md b/docs/administration/oidc/index.md index a0c33e41..0a82698e 100644 --- a/docs/administration/oidc/index.md +++ b/docs/administration/oidc/index.md @@ -1,15 +1,15 @@ --- title: OIDC Setup -description: Wire RomM up to an OpenID Connect provider for SSO and centralised user management. +description: Wire up to an OpenID Connect provider for SSO and centralised user management --- # OIDC Setup -OpenID Connect (OIDC) lets users sign in to RomM through an external identity provider: Authelia, Authentik, Keycloak, PocketID, Zitadel, Okta, Auth0, or anything standards-compliant. Benefits: single sign-on across your homelab, no RomM-specific password to manage, centralised MFA, and on 5.0 you can map OIDC groups/claims to RomM roles. +OpenID Connect (OIDC) lets users sign in to RomM through an external identity provider: Authelia, Authentik, Keycloak, PocketID, Zitadel, Okta, Auth0, or anything standards-compliant. Single sign-on across your homelab, no RomM-specific password to manage, centralised MFA, and map OIDC groups/claims to RomM roles. !!! note "OIDC is optional" - RomM has its own user system and works fine without OIDC. Enable OIDC when you already run an IdP and want RomM to follow suit. + RomM has its own user system and works fine without OIDC. Enable OIDC when you already run an IdP and want RomM to follow suit, or when you want to unify user management across multiple apps. ## How it works @@ -18,19 +18,18 @@ OpenID Connect (OIDC) lets users sign in to RomM through an external identity pr 3. They authenticate (password, passkey, MFA, whatever your provider enforces). 4. Provider redirects back to `{ROMM_BASE_URL}/api/oauth/openid` with an authorisation code. 5. RomM exchanges the code for an ID token, reads the user's email and role claims, and either creates a matching RomM user on the fly or logs an existing one in. -6. From there it's a normal RomM session: same cookies, same scope model. ## Provider guides -Pick your provider and follow the step-by-step. They all end with the same set of env vars on the RomM side. The guides just differ on how to register RomM as an application and where to find the client ID/secret. +Pick your provider and follow the step-by-step instructions. They all end with the same set of env vars on the RomM side; the guides just differ on how to register RomM as an application and where to find the client ID/secret. -- [Authelia](authelia.md): lightweight self-hosted IdP, great for homelabs. -- [Authentik](authentik.md): full-featured open-source IdP with MFA and fancy flows. -- [Keycloak](keycloak.md): the heavyweight standard, feature-complete. -- [PocketID](pocketid.md): passkey-only, minimalist. -- [Zitadel](zitadel.md): enterprise-grade open source with SAML + OIDC. +- [Authelia](authelia.md) +- [Authentik](authentik.md) +- [Keycloak](keycloak.md) +- [PocketID](pocketid.md) +- [Zitadel](zitadel.md) -Not listed? Any standards-compliant OIDC provider works: Okta, Auth0, Google Workspace, Microsoft Entra, etc. Use one of the above as a template and consult your provider's docs for the registration side. +Not listed? Most standards-compliant OIDC providers work: Okta, Auth0, Google Workspace, Microsoft Entra, etc. Use one of the above as a template and consult your provider's docs for the registration side. ## Minimum RomM config @@ -47,9 +46,9 @@ environment: - ROMM_BASE_URL=https://romm.example.com # must match your reverse-proxy URL ``` -`OIDC_REDIRECT_URI` must exactly match what you register at the provider: same scheme, host, path, no trailing slash. +`OIDC_REDIRECT_URI` must exactly match what you register at the provider (same scheme, host, path, no trailing slash). -## Role mapping (5.0) +## Role mapping By default, new OIDC users are provisioned as **Viewers**. To let your IdP assign roles based on group membership, set: @@ -67,7 +66,7 @@ Roles are re-evaluated on **every login**, so demoting someone on the IdP side t ## Autologin -Bypass the RomM login page entirely. Redirects straight to the IdP: +To bypass the RomM login page entirely and redirect straight to the IdP: ```yaml environment: @@ -90,11 +89,11 @@ environment: - OIDC_END_SESSION_ENDPOINT=https://auth.example.com/application/o/end-session/ ``` -The endpoint URL is provider-specific. The per-provider guides list it. +The endpoint URL is provider-specific, check the per-provider guides or your IdP's docs. ## Username source -By default the local part of the email (the bit before `@`) becomes the RomM username. Override with: +By default the local part of the email (the bit before `@`) becomes the RomM username, but you can override it with: ```yaml environment: @@ -104,12 +103,12 @@ environment: ## Important notes - **Email must match** between OIDC and any existing RomM account, otherwise OIDC creates a new account alongside the old one. -- **HTTPS is required** in production: OIDC will refuse to redirect to a plain-HTTP `ROMM_BASE_URL`. -- **Clock skew**: large drift between the RomM host and IdP will cause ID-token validation to fail. Run NTP. +- **HTTPS is required** in production, as OIDC will refuse to redirect to a plain-HTTP `ROMM_BASE_URL`. +- Large drift between the RomM host and IdP will lead to **clock skew** and cause ID-token validation to fail. ## Troubleshooting Common failures and fixes live in [Authentication Troubleshooting](../../troubleshooting/authentication.md). Two of the usual suspects: -- `redirect_uri_mismatch`: `OIDC_REDIRECT_URI` differs from what's registered at the provider. Even a trailing slash matters. +- `redirect_uri_mismatch`: `OIDC_REDIRECT_URI` differs from what's registered at the provider. Even a trailing slash can matter! - User created but stuck at Viewer: check `OIDC_CLAIM_ROLES` points at a claim that actually exists in the token, and the group names match exactly (case-sensitive). diff --git a/docs/administration/oidc/keycloak.md b/docs/administration/oidc/keycloak.md index 8baf9820..7710173d 100644 --- a/docs/administration/oidc/keycloak.md +++ b/docs/administration/oidc/keycloak.md @@ -1,17 +1,15 @@ --- title: OIDC with Keycloak -description: Wire RomM's SSO to Keycloak: realm, client, RomM env vars, optional role mapping. +description: Wire up SSO to Keycloak --- # OIDC with Keycloak -[Keycloak](https://www.keycloak.org/) is the heavyweight open-source IAM standard. Feature-complete, well-documented, widely deployed. The default choice when your SSO needs are serious. - -Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. +[Keycloak](https://www.keycloak.org/) is the heavyweight open-source IAM standard. Before starting, read the [OIDC Setup overview](index.md), as it covers the RomM-side settings common to every provider. ## 1. Prerequisites -Keycloak installed and running. Upstream: [Keycloak getting started](https://www.keycloak.org/getting-started). +Keycloak installed and running via their [getting started guide](https://www.keycloak.org/getting-started). Log into the **Admin Console** and either create a new realm for RomM or reuse an existing one. @@ -20,16 +18,17 @@ Log into the **Admin Console** and either create a new realm for RomM or reuse a In the Admin Console, select your realm → **Clients** → **Create client**. 1. **Client type**: `OpenID Connect`. -2. **Client ID**: `romm` (or anything unique). Click **Next**. -3. On the capability page: - - Enable **Client authentication**. - - Leave only **Standard flow** enabled. - - Click **Next**. -4. Set URLs: +2. **Client ID**: `romm` (or something unique). +3. Click **Next**. +4. On the capability page: + - Enable **Client authentication** + - Leave only **Standard flow** enabled + - Click **Next** +5. Set URLs: - **Root URL**: `https://romm.example.com` - **Valid Redirect URIs**: `https://romm.example.com/api/oauth/openid` - **Web origins**: `https://romm.example.com` -5. Save. Go to **Credentials** tab and copy the **Client Secret**. +6. Save, then head to the **Credentials** tab and copy the **Client Secret**. ## 3. Configure RomM @@ -50,11 +49,11 @@ environment: In RomM → **Profile** → set your email to the same address Keycloak has for you. -On the Keycloak side, **Admin Console → Users**: mark each RomM user's email as **verified**. Users with unverified emails will be rejected on login. +On the Keycloak side, go to **Admin Console → Users** and mark each RomM user's email as **verified**. Users with unverified emails will be rejected on login. ## 5. Test -Restart RomM and open `/login`. Click **Login with Keycloak**. You're redirected to Keycloak, authenticate, and bounce back signed into RomM. +Restart RomM, navigate to `/login` and click the **Login with OIDC** button. You're redirected to Keycloak → authenticate → bounced back and signed into RomM! If a user already exists in RomM with a matching email, they're signed into that account. Otherwise RomM creates a new account with Viewer permissions. @@ -71,7 +70,7 @@ environment: !!! warning - Before flipping this, confirm at least one Admin account can sign in via OIDC. Otherwise a broken OIDC flow locks you out. + Before flipping this, confirm at least one Admin account can sign in via OIDC, otherwise a broken OIDC flow locks you out. ## 7. (Optional) Role mapping @@ -87,4 +86,4 @@ environment: Configure Keycloak's client to include the role/group claim in the ID token (usually via a **Group Membership** or **Realm Role** client scope mapper). Values in the claim are compared against the `OIDC_ROLE_*` env vars on every login, so demoting in Keycloak takes effect on the user's next sign-in. -See [OIDC Setup → Role mapping](index.md#role-mapping-50) for the generic version. +See [OIDC Setup → Role mapping](index.md#role-mapping) for the generic version. diff --git a/docs/administration/oidc/pocketid.md b/docs/administration/oidc/pocketid.md index 1ab61a88..d6006363 100644 --- a/docs/administration/oidc/pocketid.md +++ b/docs/administration/oidc/pocketid.md @@ -1,28 +1,27 @@ --- title: OIDC with PocketID -description: Wire RomM's SSO to PocketID for passkey-only login. +description: Wire up SSO to PocketID --- # OIDC with PocketID -[PocketID](https://github.com/stonith404/pocket-id) is a minimalist OIDC provider that **only** supports passkey authentication, with no passwords. Good fit when you want passwordless login end-to-end without the complexity of Keycloak or Authentik. - -Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. +[PocketID](https://github.com/stonith404/pocket-id) is a minimalist OIDC provider that **only** supports passkey authentication, with no passwords. Before starting, read the [OIDC Setup overview](index.md), as it covers the RomM-side settings common to every provider. ## 1. Prerequisites -PocketID installed, running, and your admin passkey already registered. Upstream setup: [PocketID setup guide](https://github.com/stonith404/pocket-id#setup). +PocketID installed, running, and your admin passkey already registered via their [PocketID setup guide](https://github.com/stonith404/pocket-id#setup). ## 2. Add the client In PocketID admin: -1. **Application Configuration**: make sure **Emails Verified** is ticked. RomM requires verified emails. +1. **Application Configuration**: make sure **Emails Verified** is tickedas RomM requires verified emails. 2. Go to **OIDC Client** → **Add OIDC Client**. 3. Fill in: - **Name**: `RomM` - **Callback URLs**: `https://romm.example.com/api/oauth/openid` -4. **Save**. Stay on this page. The client secret only displays **once**. Copy both the Client ID and Client Secret now. +4. **Save**. Stay on this page as the client secret only displays **once**. +5. Copy both the Client ID and Client Secret now. ## 3. Configure RomM @@ -47,7 +46,7 @@ RomM → **Profile** → set your email to exactly the same address PocketID has ## 5. Test -Restart RomM and open `/login`. Click **Login with OIDC**. You're redirected to PocketID, tap your passkey, and bounce back signed into RomM. +Restart RomM, navigate to `/login` and click the **Login with OIDC** button. You're redirected to PocketID → authenticate → bounced back and signed into RomM! ![Login with OIDC](../../resources/pocketid/2-romm-login.png) diff --git a/docs/administration/oidc/zitadel.md b/docs/administration/oidc/zitadel.md index 44040aaf..5eee2957 100644 --- a/docs/administration/oidc/zitadel.md +++ b/docs/administration/oidc/zitadel.md @@ -1,27 +1,19 @@ --- title: OIDC with Zitadel -description: Wire RomM's SSO to Zitadel: project, application, user info inside ID token, RomM env vars. +description: Wire up SSO to Zitadel --- # OIDC with Zitadel -[Zitadel](https://zitadel.com/) is an enterprise-grade open-source IAM platform supporting OAuth2, OIDC, SAML, and passwordless. Good fit when you want an enterprise-ish IdP without running Keycloak. - -Before starting, read the [OIDC Setup overview](index.md). It covers the RomM-side settings common to every provider. +[Zitadel](https://zitadel.com/) is an enterprise-grade open-source IAM platform supporting OAuth2, OIDC, SAML, and passwordless. Before starting, read the [OIDC Setup overview](index.md), as it covers the RomM-side settings common to every provider. ## 1. Prerequisites -Zitadel installed and running. Upstream: [self-hosted deployment docs](https://zitadel.com/docs/self-hosting/deploy/overview). Change the default organization password before you go further. +Zitadel installed and running via their [self-hosted deployment docs](https://zitadel.com/docs/self-hosting/deploy/overview). Change the default organization password before you go further! ## 2. Create a project -Create a new project (e.g. `RomM`). This holds the client and its auth settings. - -On the project's **General** tab, the toggles mean: - -- **Assert Roles on Authentication**: not useful today. RomM 5.0 reads roles via `OIDC_CLAIM_ROLES` regardless. Leave off unless you're setting up role mapping. -- **Check authorization on Authentication**: recommended. If off, anyone who can register in Zitadel can sign into RomM (as Viewer). Turn this on if Zitadel registration is open. -- **Check for Project on Authentication**: only matters if you're separating users by Zitadel organization. Skip for a single RomM instance. +Create a new project (e.g. `RomM`). This holds the client and its auth settings. On the **General** tab, **Check authorization on Authentication** is recommended. If turned off, anyone who can register in Zitadel can sign into RomM (as a Viewer). Turn this on if Zitadel registration is open. ### 2.5 (Optional) Grant users to the project @@ -29,7 +21,8 @@ If you enabled **Check authorization on Authentication**: 1. **Authorization** tab → **New**. 2. Select user(s) → **Continue**. -3. "No role has been created yet" is fine: just **Save**. The user appears in the authorization list with no roles. +3. "No role has been created yet" is fine, just **Save**. +4. The user appears in the authorization list with no roles. ## 3. Create the application @@ -43,13 +36,11 @@ On the project's **General** tab, under **Applications**, click **New**. Tick ** - **Redirect URIs**: `https://romm.example.com/api/oauth/openid` - **Post Logout URIs**: `https://romm.example.com/` -Click **Create**. The **client secret is shown once**. Copy it now. +Click **Create**. The **client secret is shown once**, copy it now! ## 4. Enable claims in the ID Token -Without this, RomM throws "Email is missing from token" on login. - -Open the application's **Token Settings** tab → tick **User Info inside ID Token** → **Save**. +Without this, RomM throws "Email is missing from token" on login. Open the application's **Token Settings** tab → tick **User Info inside ID Token** → **Save**. ## 5. Configure RomM @@ -64,9 +55,9 @@ environment: - ROMM_BASE_URL=https://romm.example.com ``` -Zitadel's OIDC discovery URL is at `/.well-known/openid-configuration`, handy for debugging. +Zitadel's OIDC discovery URL is at `/.well-known/openid-configuration`, which is handy for debugging. -For role mapping from Zitadel, see [OIDC Setup → Role mapping](index.md#role-mapping-50). +For role mapping from Zitadel, see [OIDC Setup → Role mapping](index.md#role-mapping). ## 6. Set email on RomM + Zitadel @@ -74,6 +65,6 @@ In RomM → **Profile** → set your email to exactly the same address your Zita ## 7. Test -Restart RomM and open `/login`. Click **Login with Zitadel**. You're redirected, authenticate, and bounce back signed into RomM. +Restart RomM, navigate to `/login` and click the **Login with OIDC** button. You're redirected to Zitadel → authenticate → bounced back and signed into RomM! If it doesn't work, head to [Authentication Troubleshooting](../../troubleshooting/authentication.md). diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 5aaa657c..09a9096a 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -46,12 +46,6 @@ Useful for: "which platform is eating my disk?" and "which platform has the wors !!! note "Per-platform stats are opt-in" Computing per-platform stats walks the whole DB and costs real time on big libraries. The main stats load fast, and per-platform expansion loads on demand. -## What the numbers don't include - -- **Disk usage for the database itself**: MariaDB / Postgres data volume isn't reported here. Use `docker system df -v` or your volume backend's native tooling. -- **Valkey memory**: same. Monitor Valkey separately if you're tight on RAM. -- **Per-user storage breakdown**: not exposed in 5.0. - ## Using stats for capacity planning Rule-of-thumb sizes: diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index c6280708..c767ef4d 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -1,6 +1,6 @@ --- title: Users & Roles -description: User management, roles, and the scope model in RomM 5.0. +description: User management, roles, and the scope model --- # Users & Roles @@ -15,7 +15,7 @@ RomM is multi-user from the start. The first user created during Setup is always | **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | | **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | -Roles are a convenience layer on top of **scopes**; see the scope matrix below for exactly what each role grants. You can't create custom roles in 5.0, so if you need finer-grained access, use the most restrictive role and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. +Roles are a convenience layer on top of **scopes**; see the scope matrix below for exactly what each role grants. You can't create custom roles (yet), so if you need finer-grained access, use the most restrictive role and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. ## Scope matrix From 3a3e340d1e9d5fe8159d156f46182c032e690453 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 22:34:02 -0400 Subject: [PATCH 042/121] docs: drop trailing periods from fragment bullets Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/about/brand-guidelines.md | 6 +- docs/about/credits.md | 10 +- docs/about/faqs.md | 14 +- docs/about/license.md | 6 +- docs/administration/administration-page.md | 105 ++---------- docs/administration/authentication.md | 22 +-- docs/administration/firmware-management.md | 12 +- docs/administration/index.md | 30 ++-- .../invitations-and-registration.md | 2 +- docs/administration/metadata-providers.md | 12 +- docs/administration/observability.md | 48 +++--- docs/administration/oidc/authentik.md | 2 +- docs/administration/scanning-and-watcher.md | 26 +-- docs/administration/scheduled-tasks.md | 34 ++-- docs/administration/server-stats.md | 8 +- docs/administration/ssh-sync.md | 10 +- docs/administration/users-and-roles.md | 4 +- docs/developers/api-authentication.md | 14 +- docs/developers/api-reference.md | 150 +++++++++--------- docs/developers/architecture.md | 70 ++++---- docs/developers/contributing.md | 12 +- docs/developers/development-setup.md | 8 +- docs/developers/i18n.md | 22 +-- docs/developers/index.md | 36 ++--- docs/developers/openapi.md | 18 +-- docs/developers/releasing.md | 30 ++-- docs/developers/websockets.md | 14 +- docs/ecosystem/argosy.md | 28 ++-- docs/ecosystem/client-api-tokens.md | 16 +- docs/ecosystem/community-apps.md | 18 +-- docs/ecosystem/device-sync-protocol.md | 14 +- docs/ecosystem/fpkgi.md | 16 +- docs/ecosystem/grout.md | 38 ++--- docs/ecosystem/igir.md | 16 +- docs/ecosystem/index.md | 52 +++--- docs/ecosystem/kekatsu.md | 10 +- docs/ecosystem/muos-app.md | 18 +-- docs/ecosystem/pkgj.md | 14 +- docs/ecosystem/playnite-plugin.md | 18 +-- docs/ecosystem/tinfoil.md | 22 +-- docs/ecosystem/webrcade.md | 8 +- docs/getting-started/first-scan.md | 12 +- docs/getting-started/folder-structure.md | 8 +- docs/getting-started/quick-start.md | 4 +- docs/index.md | 2 +- docs/install/backup-and-restore.md | 2 +- docs/install/databases.md | 4 +- docs/install/docker-compose.md | 14 +- docs/install/index.md | 12 +- docs/install/kubernetes.md | 8 +- docs/install/redis-or-valkey.md | 16 +- docs/install/synology.md | 6 +- docs/install/truenas.md | 10 +- docs/install/unraid.md | 10 +- docs/platforms/custom-platforms.md | 10 +- docs/platforms/emulatorjs-config.md | 22 +-- docs/platforms/firmware-by-platform.md | 12 +- docs/platforms/index.md | 26 +-- docs/platforms/ms-dos.md | 16 +- docs/platforms/ruffle-config.md | 16 +- docs/platforms/supported-platforms.md | 12 +- docs/reference/configuration-file.md | 12 +- docs/reference/environment-variables.md | 14 +- docs/reference/exports.md | 8 +- docs/reference/feeds.md | 14 +- docs/reference/ports-and-endpoints.md | 6 +- docs/reference/scheduled-tasks.md | 24 +-- docs/releases/index.md | 14 +- docs/troubleshooting/authentication.md | 6 +- docs/troubleshooting/in-browser-play.md | 18 +-- docs/troubleshooting/index.md | 24 +-- docs/troubleshooting/kubernetes.md | 4 +- docs/troubleshooting/miscellaneous.md | 4 +- docs/troubleshooting/netplay.md | 12 +- docs/troubleshooting/sync.md | 20 +-- docs/using/account-and-profile.md | 18 +-- docs/using/collections.md | 14 +- docs/using/console-mode.md | 26 +-- docs/using/in-browser-play.md | 10 +- docs/using/index.md | 28 ++-- docs/using/languages.md | 14 +- docs/using/library.md | 76 ++++----- docs/using/mobile-and-tv.md | 24 +-- docs/using/netplay.md | 6 +- docs/using/pwa.md | 10 +- docs/using/retroachievements.md | 12 +- docs/using/rom-patcher.md | 4 +- docs/using/saves-and-states.md | 32 ++-- docs/using/smart-collections.md | 44 ++--- docs/using/virtual-collections.md | 10 +- 90 files changed, 829 insertions(+), 914 deletions(-) diff --git a/docs/about/brand-guidelines.md b/docs/about/brand-guidelines.md index b2b6baf7..33b203fe 100644 --- a/docs/about/brand-guidelines.md +++ b/docs/about/brand-guidelines.md @@ -75,9 +75,9 @@ If you're building something that integrates with RomM and would like to use / r The logo assets live at [rommapp/romm/tree/master/frontend/assets/](https://github.com/rommapp/romm/tree/master/frontend/assets/): -- `isotipo.svg` / `isotipo.png`: the mark (circular logo). -- `logotipo.svg` / `logotipo.png`: the wordmark. -- `social_preview.png`: GitHub social preview. +- `isotipo.svg` / `isotipo.png`: the mark (circular logo) +- `logotipo.svg` / `logotipo.png`: the wordmark +- `social_preview.png`: GitHub social preview ## Questions diff --git a/docs/about/credits.md b/docs/about/credits.md index 4ca4fd58..c598c0cd 100644 --- a/docs/about/credits.md +++ b/docs/about/credits.md @@ -76,9 +76,9 @@ RomM stands on an enormous amount of open-source work. In rough order of "how vi ### Docs stack -- [MkDocs](https://www.mkdocs.org/) + [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/): what you're reading. -- [mike](https://github.com/jimporter/mike): docs versioning. -- [asciinema](https://asciinema.org/): terminal recordings. +- [MkDocs](https://www.mkdocs.org/) + [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/): what you're reading +- [mike](https://github.com/jimporter/mike): docs versioning +- [asciinema](https://asciinema.org/): terminal recordings ## Missing? @@ -86,5 +86,5 @@ Open a PR against this page. Credit is cheap, and we want to give it where it's ## See also -- [License](license.md): the legal bit. -- [Contributing](../developers/contributing.md): if you want to be on this list. +- [License](license.md): the legal bit +- [Contributing](../developers/contributing.md): if you want to be on this list diff --git a/docs/about/faqs.md b/docs/about/faqs.md index aeb10df6..ab5af111 100644 --- a/docs/about/faqs.md +++ b/docs/about/faqs.md @@ -21,7 +21,7 @@ Yes! Licensed under [AGPL-3.0](license.md), the core will always be free. Other ## How does it compare to [X other manager]? -RomM emphasises self-hosted + multi-user + in-browser-play + the companion-app ecosystem. If those matter, try RomM; if you just want a local Windows app that scans a folder, tools like LaunchBox may fit better. +RomM emphasises self-hosted + multi-user + in-browser-play + the companion-app ecosystem. Try RomM if those matter, but if you just want a local Windows app that scans a folder, tools like LaunchBox may fit better. ## Do I need metadata API keys? @@ -51,8 +51,8 @@ Not supported: ## How much RAM / CPU does it need? -- **Minimum** (small library, 1 user): 512 MB RAM, any modest CPU. -- **Comfortable** (thousands of ROMs, a few users, occasional scans): 2 GB RAM, 2 cores. +- **Minimum** (small library, 1 user): 512 MB RAM, any modest CPU +- **Comfortable** (thousands of ROMs, a few users, occasional scans): 2 GB RAM, 2 cores Heaviest CPU usage is during scans, as hashing and network-bound metadata calls may cause spikes. @@ -81,7 +81,7 @@ Full troubleshooting steps can be found in [Scanning Troubleshooting](../trouble ## My scan finds platforms but no games inside them. -This is almost always a mount-depth issue. RomM expects the *parent* of your `roms/` folder mounted to `/romm/library`, not the `roms/` folder itself. If your files live at `/opt/romm/library/roms/gbc/game.gbc`, mount `/opt/romm/library` to `/romm/library`, then re-scan. +This is almost always a mount-depth issue. RomM expects the _parent_ of your `roms/` folder mounted to `/romm/library`, not the `roms/` folder itself. If your files live at `/opt/romm/library/roms/gbc/game.gbc`, mount `/opt/romm/library` to `/romm/library`, then re-scan. See [Folder Structure](../getting-started/folder-structure.md) and [Scanning Troubleshooting](../troubleshooting/scanning.md) for the full layout and common mount mistakes. @@ -138,9 +138,9 @@ Absolutely, just set `KIOSK_MODE=true` in your environment variables and anonymo You need internet for: -- First-time scan with metadata providers (they're online APIs). -- Pulling the Docker image on install or upgrade. -- OIDC login (if you use a cloud IdP). +- First-time scan with metadata providers (they're online APIs) +- Pulling the Docker image on install or upgrade +- OIDC login (if you use a cloud IdP) After the initial setup, browsing and playing can work offline. In-browser play downloads the emulator bundle on first launch, then caches it. diff --git a/docs/about/license.md b/docs/about/license.md index f27d5705..bb9c7bf8 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -75,6 +75,6 @@ Full contributor terms: [Contributing → Licensing](../developers/contributing. ## See also -- [Credits](credits.md): the humans and projects behind RomM. -- [AGPL-3.0 overview](https://choosealicense.com/licenses/agpl-3.0/): plain-language explainer. -- Full license text: [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE). +- [Credits](credits.md): the humans and projects behind RomM +- [AGPL-3.0 overview](https://choosealicense.com/licenses/agpl-3.0/): plain-language explainer +- Full license text: [LICENSE](https://github.com/rommapp/romm/blob/master/LICENSE) diff --git a/docs/administration/administration-page.md b/docs/administration/administration-page.md index 5551e264..65ffd08f 100644 --- a/docs/administration/administration-page.md +++ b/docs/administration/administration-page.md @@ -1,107 +1,22 @@ --- title: Administration Page -description: A tour of the in-app Administration UI, where every operator control lives. +description: A tour of the in-app administration UI --- # Administration Page -Click your **profile avatar** (top right, any page) to open the settings drawer. The links you see depend on your role. Admins see everything, and Editors and Viewers see a subset. - -This page is a map of what's behind each link. The deep mechanics of each feature live on their own pages. This is where to click. +Click your **profile avatar** (bottom left on any page) to open the settings drawer. The links you see depend on your role. Admins see everything, and Editors and Viewers see a subset. This page is a map of what's behind each link, where the deep mechanics of each feature live on their own pages. ## The drawer -| Link | Who sees it | What's there | -| ---------------------- | --------------------- | ------------------------------------------------------------------------------------------------------- | -| **Profile** | Everyone | Change own username, email, password, avatar. Link a RetroAchievements account and sync achievements. | -| **User Interface** | Everyone | Locale, theme (dark/light/auto), game card layout, home dashboard ribbons, collection display settings. | -| **Library Management** | Editors + Admins | Platform bindings & version mappings, missing-ROMs tool, library folder settings. | -| **Metadata Sources** | Admins | Credentials for the 13 metadata providers, scan priority. | -| **Administration** | Admins | Users, Client API Tokens, Tasks. The main admin hub. | -| **Client API Tokens** | Everyone (own tokens) | Each user's personal API tokens. Admins see a separate "all tokens" view under Administration. | -| **Server Stats** | Admins | Numbers: platforms, games, saves, states, screenshots, disk usage. | -| **About** | Everyone | RomM version, links to Discord / GitHub / docs. | - -## Profile - -The thing every user touches. - -- **Username / email / password**: self-serve changes. Password changes require the current password. -- **Avatar**: upload a small image, displayed next to your name everywhere. -- **RetroAchievements**: set your RA username to link accounts. "Sync now" pulls fresh progression data. - -See [Users & Roles](users-and-roles.md) for what role-specific self-serve is allowed. - -## User Interface - -Per-user UI preferences. Stored in the user's row + localStorage, not in `config.yml`. - -- **Language**: 19 locales supported. See [Languages](../using/languages.md) for the list. -- **Theme**: Dark, Light, or Auto (follows OS preference). Palette overrides via `extra_css` are operator-level. -- **Game card layout**: cover style (2D, 3D boxart, poster), info-density, the `vanilla-tilt` 3D hover effect on/off. -- **Home dashboard ribbons**: show/hide "Recently Added", "Continue Playing", "Collections", etc. -- **Virtual collections**: enable/disable auto-generated groupings (by genre, developer, year, etc.). - -## Library Management - -Editor-grade tools for catalogue hygiene. - -- **Platform bindings**: map a filesystem folder name (`super_nintendo/`) to a platform slug (`snes`). Mirrors `system.platforms` in `config.yml`. -- **Platform versions**: some platforms have multiple IGDB "versions" (e.g. Mega Drive vs Genesis). Pin which one RomM uses for lookups. -- **Missing ROMs**: a filter/table view showing DB entries whose files are gone. Bulk-delete from here, or run the [Cleanup Missing ROMs](scheduled-tasks.md#cleanup-missing-roms-manual) task. - -## Metadata Sources - -Admin-only. Per-provider credentials, test buttons, and the priority-ordering drag-and-drop UI. Equivalent to editing the `*_CLIENT_ID` / `*_API_KEY` env vars and `scan.priority` in `config.yml` but interactive. - -Full provider details in [Metadata Providers](metadata-providers.md). - -## Administration - -The main admin hub. Three sub-panels: - -### Users - -- Table of all users with role, last login, creation date. -- **Add**: manual user creation with username + email + password + role. -- **Invite**: generate an invite link. See [Invitations & Registration](invitations-and-registration.md). -- **Edit**: change username, email, role, password. Reset password by typing a new one. -- **Delete**: red trash icon. RomM won't let you delete yourself or the last admin. - -### Client API Tokens - -- Table of every token on the server (admin view, users see only their own via their Profile). -- Filter by user. Revoke any token. -- See [Authentication → Client API Tokens](authentication.md#client-api-tokens) for the create-your-own flow. - -### Tasks - -- Status of every scheduled / manual / watcher task: queued, running, idle, failed. -- **Run** button per task (requires `tasks.run` scope). -- See [Scheduled Tasks](scheduled-tasks.md) for what each one does. - -## Server Stats - -Admin-only. A dashboard of counts and sizes: - -- Total platforms, games, saves, states, screenshots. -- Total disk footprint (library + resources + assets). -- Per-platform breakdown: handy for spotting a platform that's ballooned. - -Full details in [Server Stats](server-stats.md). - -## Keyboard shortcuts - -A few useful ones wherever the drawer is open: - -| Key | Action | -| ----- | ------------------------------------- | -| `Esc` | Close the drawer. | -| `g h` | Go home. | -| `g s` | Open the Search page. | -| `g c` | Focus the Scan button in the sidebar. | - -Full shortcut reference is on the [Using RomM](../using/index.md) page. +- **Profile**: Change own username, email, password, avatar, and link a RetroAchievements account to sync achievements. +- **User Interface**: Locale, theme (dark/light/auto), game card layout, home dashboard ribbons, collection display settings +- **Library Management**: Platform bindings & version mappings, missing-ROMs tool, library folder settings +- **Metadata Sources**: Credentials for the 13 metadata providers, scan priority +- **Administration**: Users, Client API Tokens, Tasks. The main admin hub +- **Client API Tokens**: Each user's personal API tokens. Admins see a separate "all tokens" view under Administration. +- **Server Stats**: Platforms, games, saves, states, screenshots, disk usage +- **About**: RomM version, links to Discord / GitHub / docs ## Role-based visibility cheat sheet diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index 428ab5f6..85b684d5 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -9,11 +9,11 @@ This page is the **operator-side** authentication reference: knobs you turn on t Authentication flows RomM supports: -- **Username + password** (default): local account, bcrypt-hashed, stored in the DB. +- **Username + password** (default): local account, bcrypt-hashed, stored in the DB - **OIDC**: single sign-on via an external IdP. See [OIDC Setup](oidc/index.md). -- **Client API Tokens**: long-lived per-user tokens for companion apps and scripts. -- **Device pairing**: short codes for bootstrapping a token onto a handheld, covered in [Client API Tokens](../ecosystem/client-api-tokens.md). -- **Kiosk mode**: unauthenticated read-only access, handy for public demos and shared terminals. +- **Client API Tokens**: long-lived per-user tokens for companion apps and scripts +- **Device pairing**: short codes for bootstrapping a token onto a handheld, covered in [Client API Tokens](../ecosystem/client-api-tokens.md) +- **Kiosk mode**: unauthenticated read-only access, handy for public demos and shared terminals ## Session config @@ -73,10 +73,10 @@ For anything long-lived (a companion app, a cron job, a script) use **Client API Create from **Administration → Client API Tokens**. Each token: -- Belongs to a specific user. -- Carries a **subset** of that user's scopes (you choose which at creation time). -- Has an optional expiry (no expiry = never expires until manually revoked). -- Can be "paired" to a device via a short code (covered in [Client API Tokens](../ecosystem/client-api-tokens.md)). +- Belongs to a specific user +- Carries a **subset** of that user's scopes (you choose which at creation time) +- Has an optional expiry (no expiry = never expires until manually revoked) +- Can be "paired" to a device via a short code (covered in [Client API Tokens](../ecosystem/client-api-tokens.md)) Each user gets up to 25 active tokens, revokable from the same page. The API side ("how do I send this thing in a request?") lives in [API Authentication](../developers/api-authentication.md). @@ -91,9 +91,9 @@ environment: Appropriate for: -- Shared-terminal demos. -- Public-facing "display" instances (e.g. a wall-mounted browse-only catalogue). -- `demo.romm.app`. +- Shared-terminal demos +- Public-facing "display" instances (e.g. a wall-mounted browse-only catalogue) +- `demo.romm.app` Authenticated users (when you do sign in) still see their full role. Kiosk only affects anonymous traffic. diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index 6623a958..8b4ba3b7 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -9,8 +9,8 @@ Many emulated platforms require BIOS or firmware to boot: PlayStation (SCPH1001) Firmware is **not** ROM. Keep the two separate: -- `/romm/library/roms/`: games you own dumps of. -- `/romm/library/bios/` (Structure A) or `/romm/library/{platform}/bios/` (Structure B): system firmware. +- `/romm/library/roms/`: games you own dumps of +- `/romm/library/bios/` (Structure A) or `/romm/library/{platform}/bios/` (Structure B): system firmware Legality varies by jurisdiction. RomM does not ship firmware and the project cannot help you obtain it. @@ -67,10 +67,10 @@ File naming matters: emulators look for specific filenames. Double-check against Firmware has a standard REST surface under `/api/firmware/`: -- `GET /api/firmware`: list (optional `?platform_id=` filter). -- `POST /api/firmware`: upload. -- `GET /api/firmware/{id}/content/{filename}`: download. -- `POST /api/firmware/delete`: bulk delete. +- `GET /api/firmware`: list (optional `?platform_id=` filter) +- `POST /api/firmware`: upload +- `GET /api/firmware/{id}/content/{filename}`: download +- `POST /api/firmware/delete`: bulk delete Requires `firmware.read` / `firmware.write` scopes. See the [API Reference](../developers/api-reference.md). diff --git a/docs/administration/index.md b/docs/administration/index.md index 031d9bc0..ebc49a85 100644 --- a/docs/administration/index.md +++ b/docs/administration/index.md @@ -13,33 +13,33 @@ The end-user equivalent (how to actually play the games, build collections, uplo ### Users & access -- **[Users & Roles](users-and-roles.md)**: roles, the 19-scope model, how permissions add up. -- **[Invitations & Registration](invitations-and-registration.md)**: invite links, public signup, first-user setup. -- **[Authentication](authentication.md)**: session config, password reset, Client API Tokens for devices. -- **[OIDC Setup](oidc/index.md)**: Authelia, Authentik, Keycloak, PocketID, Zitadel, SSO + role mapping. +- **[Users & Roles](users-and-roles.md)**: roles, the 19-scope model, how permissions add up +- **[Invitations & Registration](invitations-and-registration.md)**: invite links, public signup, first-user setup +- **[Authentication](authentication.md)**: session config, password reset, Client API Tokens for devices +- **[OIDC Setup](oidc/index.md)**: Authelia, Authentik, Keycloak, PocketID, Zitadel, SSO + role mapping ### Content & library -- **[Metadata Providers](metadata-providers.md)**: all 13 providers, credentials, priority ordering. -- **[Scanning & Watcher](scanning-and-watcher.md)**: how scans work, scan modes, filesystem watcher. -- **[Firmware Management](firmware-management.md)**: BIOS/firmware uploads for emulation. +- **[Metadata Providers](metadata-providers.md)**: all 13 providers, credentials, priority ordering +- **[Scanning & Watcher](scanning-and-watcher.md)**: how scans work, scan modes, filesystem watcher +- **[Firmware Management](firmware-management.md)**: BIOS/firmware uploads for emulation ### Operations -- **[Scheduled Tasks](scheduled-tasks.md)**: what runs in the background and how to tune it. -- **[Server Stats](server-stats.md)**: the stats page and what its numbers mean. -- **[Observability](observability.md)**: logs, Sentry, OpenTelemetry, `/heartbeat`. -- **[SSH Sync](ssh-sync.md)**: push/pull sync to handhelds and other devices. -- **[Administration Page](administration-page.md)**: the in-app admin UI tour. +- **[Scheduled Tasks](scheduled-tasks.md)**: what runs in the background and how to tune it +- **[Server Stats](server-stats.md)**: the stats page and what its numbers mean +- **[Observability](observability.md)**: logs, Sentry, OpenTelemetry, `/heartbeat` +- **[SSH Sync](ssh-sync.md)**: push/pull sync to handhelds and other devices +- **[Administration Page](administration-page.md)**: the in-app admin UI tour ### Configuration -- **[Environment Variables](../reference/environment-variables.md)**: every env var, grouped by area. -- **[Configuration File](../reference/configuration-file.md)**: the `config.yml` schema. +- **[Environment Variables](../reference/environment-variables.md)**: every env var, grouped by area +- **[Configuration File](../reference/configuration-file.md)**: the `config.yml` schema ### Keeping data safe -- **[Backup & Restore](../install/backup-and-restore.md)**: routine backups, restore drill, host migration. +- **[Backup & Restore](../install/backup-and-restore.md)**: routine backups, restore drill, host migration ## The role model in thirty seconds diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md index 9ce0e51d..92f7c60e 100644 --- a/docs/administration/invitations-and-registration.md +++ b/docs/administration/invitations-and-registration.md @@ -59,7 +59,7 @@ When on, the login page grows a "Register" link and `/register` becomes an open Appropriate for: - Instances behind auth at the reverse-proxy layer (Authelia, Cloudflare Access, an IP allowlist). RomM's registration is just paperwork once the proxy has already authenticated the visitor. -- Truly public or group-shared instances where you genuinely want open signup. +- Truly public or group-shared instances where you genuinely want open signup Inappropriate for everything else. **If RomM is exposed to the internet with no upstream auth, leave this off**: it's the single fastest way to fill your DB with spam accounts. diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md index f400a5c4..e8967e79 100644 --- a/docs/administration/metadata-providers.md +++ b/docs/administration/metadata-providers.md @@ -13,7 +13,7 @@ Configure providers either via env vars (below) or interactively in **Administra ### ⭐ The Chef's Choice: Hasheous + IGDB + SteamGridDB + RetroAchievements -- Covers 135+ popular systems. +- Covers 135+ popular systems - **Hasheous** does hash-based matching and proxies IGDB data (titles, descriptions, artwork). - **IGDB** adds related games, screenshots, and broader metadata. - **SteamGridDB** provides high-quality alternative cover art (opt-in per game via the "search cover" button). @@ -24,7 +24,7 @@ Configure providers either via env vars (below) or interactively in **Administra ### ⭐ The French Connection: ScreenScraper + RetroAchievements -- Covers 125+ popular systems. +- Covers 125+ popular systems - **ScreenScraper** provides titles, descriptions, cover art (2D + optional 3D + CD), screenshots, manuals. Also supports hash-based matching since RomM 4.4. - **RetroAchievements** overlays achievement progress. - **Pick this if you want to avoid anything Twitch/Amazon-owned.** @@ -33,14 +33,14 @@ Configure providers either via env vars (below) or interactively in **Administra ### The Twitch Fanboy: IGDB + PlayMatch -- Covers the 200+ systems IGDB knows about. -- IGDB-only metadata with PlayMatch's community-hosted hash-matching service bolted on for unmatched files. +- Covers the 200+ systems IGDB knows about +- IGDB-only metadata with PlayMatch's community-hosted hash-matching service bolted on for unmatched files - **Use if you specifically want a single-provider solution backed by IGDB.** ### The Quick Starter: Hasheous only -- Hash-based matching, fast scans, no API keys required. -- Proxies titles/descriptions/artwork from IGDB. +- Hash-based matching, fast scans, no API keys required +- Proxies titles/descriptions/artwork from IGDB - **For users who want to avoid the IGDB/Twitch registration dance.** ## Setup instructions diff --git a/docs/administration/observability.md b/docs/administration/observability.md index 2e1a8e2a..ebe6fa85 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -7,10 +7,10 @@ description: Logs, Sentry error tracking, OpenTelemetry, and the /heartbeat endp Four ways to know what RomM is doing: -- **Container logs**: always available, the first stop. -- **`/api/heartbeat`**: health + config summary for uptime monitors. -- **Sentry**: opt-in error tracking with stack traces. -- **OpenTelemetry**: opt-in distributed tracing + metrics. +- **Container logs**: always available, the first stop +- **`/api/heartbeat`**: health + config summary for uptime monitors +- **Sentry**: opt-in error tracking with stack traces +- **OpenTelemetry**: opt-in distributed tracing + metrics ## Logs @@ -55,13 +55,13 @@ GET /api/heartbeat Returns: -- RomM version. -- Whether the Setup Wizard is still pending. -- Which metadata providers are enabled. -- Which platforms have data. -- OIDC config (redacted credentials). -- Scheduled-task schedule summary. -- Watcher status. +- RomM version +- Whether the Setup Wizard is still pending +- Which metadata providers are enabled +- Which platforms have data +- OIDC config (redacted credentials) +- Scheduled-task schedule summary +- Watcher status Wire this to your uptime monitor. A failure here is real: the process is down or the DB/Valkey is unreachable. @@ -94,13 +94,13 @@ environment: What's sent: -- Stack traces for unhandled exceptions. -- Per-request timing on slow endpoints. -- Redacted URL parameters (secrets stripped). +- Stack traces for unhandled exceptions +- Per-request timing on slow endpoints +- Redacted URL parameters (secrets stripped) What's not sent: ROM filenames, user credentials, metadata provider API keys. RomM filters sensitive parameters before reporting. -Suitable for self-hosted Sentry or [sentry.io](https://sentry.io/); drop the DSN to stop reporting. +Suitable for self-hosted Sentry or [sentry.io](https://sentry.io/). ## OpenTelemetry @@ -116,13 +116,13 @@ environment: Standard [OTEL env vars](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) apply. RomM emits: -- **Traces**: HTTP request spans, DB query spans, RQ job spans. -- **Metrics**: request counts, durations, queue depth, scan progress. -- **Logs**: structured log correlation with trace IDs. +- **Traces**: HTTP request spans, DB query spans, RQ job spans +- **Metrics**: request counts, durations, queue depth, scan progress +- **Logs**: structured log correlation with trace IDs Exporters: -- OTLP gRPC (default, port `4317`). +- OTLP gRPC (default, port `4317`) - OTLP HTTP (port `4318`): set `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`. Send to an OpenTelemetry Collector, then fan out to Tempo / Jaeger / Honeycomb / Datadog / Grafana Cloud / whatever you run. @@ -148,11 +148,11 @@ Returns an array of every scheduled / manual / watcher task with current status For a homelab instance: -- Default `INFO` logs into the container logs → forwarded to Loki / Promtail / whatever you already run. -- `/api/heartbeat` hit every 60 seconds from Uptime Kuma / Gatus. +- Default `INFO` logs into the container logs → forwarded to Loki / Promtail / whatever you already run +- `/api/heartbeat` hit every 60 seconds from Uptime Kuma / Gatus For a serious deployment: -- Above, plus Sentry DSN configured. -- Plus OpenTelemetry to the collector you already have. -- Alert on: heartbeat failure, task stuck > 1 h, `ERROR`-level log spikes. +- Above, plus Sentry DSN configured +- Plus OpenTelemetry to the collector you already have +- Alert on: heartbeat failure, task stuck > 1 h, `ERROR`-level log spikes diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md index 2458a390..c895ca8e 100644 --- a/docs/administration/oidc/authentik.md +++ b/docs/administration/oidc/authentik.md @@ -68,7 +68,7 @@ Click **Create**. - **Name**: `RomM` - **Slug**: `romm` -- **Provider**: the `RomM OIDC Provider` you just made. +- **Provider**: the `RomM OIDC Provider` you just made ![New application](../../resources/authentik/6-new-application.png) diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 60e3d59e..21fd646d 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -19,10 +19,10 @@ Every scan picks one mode. Modes differ in what they touch, so use the most-targ | Mode | What it does | When to use | | ----------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set; it's fast. | +| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set (very fast). | | **Quick** | Skips files that already exist in the DB, with no metadata refresh. | Default for scheduled runs and the watcher. | | **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | -| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured); rare. | +| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured). | | **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash (pre-4.4) or when you suspect file corruption. | | **Complete** | Full rescan, recalculating hashes and re-fetching metadata for everything. | Rarely, since it takes a long time. | @@ -32,11 +32,11 @@ You can further scope a scan to specific **platforms** and specific **metadata p **Scan button in the sidebar.** The Scan page shows: -- Platform checkboxes (select which to scan, leave empty to scan all). -- Metadata provider toggles (overrides the default priority for this one scan). -- Advanced options: skip hashing (helpful on low-power hosts), target a LaunchBox refresh. -- A live log of everything the scanner is doing. -- Per-platform progress panels with matched / unmatched / missing counts. +- Platform checkboxes (select which to scan, leave empty to scan all) +- Metadata provider toggles (overrides the default priority for this one scan) +- Advanced options: skip hashing (helpful on low-power hosts), target a LaunchBox refresh +- A live log of everything the scanner is doing +- Per-platform progress panels with matched / unmatched / missing counts A running scan survives browser refreshes, and the log streams over Socket.IO. Multiple admins opening the page see the same scan state. @@ -67,12 +67,12 @@ environment: Behaviour: -- Watches `/romm/library` (and everything under it) recursively. +- Watches `/romm/library` (and everything under it) recursively - Debounces bursts of events: the delay (default 10 seconds) lets a large `cp` or `rsync` settle before scanning. -- Batches scans intelligently: many events → a single consolidated scan, not one scan per file. -- Ignores content modifications and metadata-only changes, caring only about files appearing or disappearing (not `chmod`). -- Skips OS noise (`.DS_Store`, `Thumbs.db`, `.tmp`, etc.). -- If a whole new platform folder appears, switches to a **New Platforms** scan to pick it up cleanly. +- Batches scans intelligently: many events → a single consolidated scan, not one scan per file +- Ignores content modifications and metadata-only changes, caring only about files appearing or disappearing (not `chmod`) +- Skips OS noise (`.DS_Store`, `Thumbs.db`, `.tmp`, etc.) +- If a whole new platform folder appears, switches to a **New Platforms** scan to pick it up cleanly ### When **not** to enable the watcher @@ -127,7 +127,7 @@ When a metadata provider returns multiple regional variants (Japanese cover, US ## Metadata source priority -Who wins when two providers disagree is covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution); short version: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. +Who wins when two providers disagree is covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution), though the short version is: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. ## Troubleshooting diff --git a/docs/administration/scheduled-tasks.md b/docs/administration/scheduled-tasks.md index ccfe0617..0f9922af 100644 --- a/docs/administration/scheduled-tasks.md +++ b/docs/administration/scheduled-tasks.md @@ -7,10 +7,10 @@ description: What RomM runs in the background, how to reschedule it, and how to RomM runs background work through **RQ** (Redis Queue). Tasks fall into four categories: -- **Scheduled**: cron-driven, run on their own. -- **Watcher**: triggered by filesystem events. -- **Manual**: admin-triggered from the UI or API. -- **Enqueued**: side effects of user actions (a scan started from the Scan page, a metadata refresh on a ROM edit). Not listed here. +- **Scheduled**: cron-driven, run on their own +- **Watcher**: triggered by filesystem events +- **Manual**: admin-triggered from the UI or API +- **Enqueued**: side effects of user actions (a scan started from the Scan page, a metadata refresh on a ROM edit). Not listed here For the lookup-only reference (every task, its cron default, its env var), see [Scheduled Tasks Reference](../reference/scheduled-tasks.md). This page is the narrative: what each one is for, when to worry, and how to tune. @@ -28,10 +28,10 @@ minute hour day-of-month month day-of-week Examples: -- `0 3 * * *`: 3 AM daily. -- `0 */6 * * *`: every 6 hours, on the hour. -- `*/30 * * * *`: every 30 minutes. -- `0 2 * * 0`: 2 AM every Sunday. +- `0 3 * * *`: 3 AM daily +- `0 */6 * * *`: every 6 hours, on the hour +- `*/30 * * * *`: every 30 minutes +- `0 2 * * 0`: 2 AM every Sunday Set the env var, restart the container. Alembic runs on every start, and the scheduler picks up the new schedule the moment RomM comes back up. @@ -52,15 +52,15 @@ Three paths: **Administration → Tasks** shows every task with a Run button. Admins (anyone with `tasks.run` scope) can trigger: -- Folder Scan (launches the Scan page with the task pre-started). -- Cleanup Missing ROMs. -- Cleanup Orphaned Resources. -- LaunchBox Metadata Sync. -- Switch titleDB Fetch. -- Image Conversion. -- RetroAchievements Sync. -- Netplay Cleanup. -- Push-Pull Device Sync. +- Folder Scan (launches the Scan page with the task pre-started) +- Cleanup Missing ROMs +- Cleanup Orphaned Resources +- LaunchBox Metadata Sync +- Switch titleDB Fetch +- Image Conversion +- RetroAchievements Sync +- Netplay Cleanup +- Push-Pull Device Sync ### From the API diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 09a9096a..42bcea39 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -35,10 +35,10 @@ A breakdown of disk usage by directory: Under the summary, an expandable table sorted by either ROM count or disk usage. Click a platform to drill into: -- Matched / unmatched count. -- Region distribution (how many games tagged USA, Japan, Europe, World, etc.). -- Language distribution. -- Metadata coverage: how many games have each field populated (cover, description, release date, rating). +- Matched / unmatched count +- Region distribution (how many games tagged USA, Japan, Europe, World, etc.) +- Language distribution +- Metadata coverage: how many games have each field populated (cover, description, release date, rating) Useful for: "which platform is eating my disk?" and "which platform has the worst match rate?" diff --git a/docs/administration/ssh-sync.md b/docs/administration/ssh-sync.md index c4cc3320..b22ab556 100644 --- a/docs/administration/ssh-sync.md +++ b/docs/administration/ssh-sync.md @@ -15,7 +15,7 @@ The client side (a handheld running Grout, a SteamDeck running DeckRommSync, etc ## When you need SSH sync -- A handheld running custom firmware exposing SSH (muOS, NextUI, Batocera, etc.). +- A handheld running custom firmware exposing SSH (muOS, NextUI, Batocera, etc.) - You want RomM to automatically copy saves/states to the device and pull them back when a session ends. - Your sync runs on a schedule, not on-demand. @@ -68,10 +68,10 @@ Copy the **public** key (`~/romm-sync-key.pub` from step 1 above) to the device' Device registration is done through a companion app (typically Grout itself) using the [Client API Token pairing flow](../ecosystem/client-api-tokens.md). Once registered, RomM knows: -- Device name + type. -- Hostname / IP. -- SSH port (default `22`). -- Target paths on the device for saves, states, and any other synced assets. +- Device name + type +- Hostname / IP +- SSH port (default `22`) +- Target paths on the device for saves, states, and any other synced assets ### 3. Test the connection diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index c767ef4d..3da82ef2 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -15,7 +15,7 @@ RomM is multi-user from the start. The first user created during Setup is always | **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | | **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | -Roles are a convenience layer on top of **scopes**; see the scope matrix below for exactly what each role grants. You can't create custom roles (yet), so if you need finer-grained access, use the most restrictive role and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. +Roles are a convenience layer on top of **scopes** (see the scope matrix below for exactly what each role grants). You can't create custom roles (yet), so if you need finer-grained access, use the most restrictive role and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. ## Scope matrix @@ -69,7 +69,7 @@ If you've wired up OIDC, new identities can be provisioned on first login. Role ## Editing and deleting users -- **Change role**: Admin → Users → Edit → Role dropdown, taking effect on next login. +- **Change role**: Admin → Users → Edit → Role dropdown, taking effect on next login - **Reset password**: Admin → Users → Edit → New password. For self-service, the user can use the "Forgot password" flow from the login page if email is configured. - **Delete**: Admin → Users → red delete icon → confirm. RomM won't let you delete the last admin or delete yourself while signed in. diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index b5fb5083..938c9448 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -7,12 +7,12 @@ description: How to authenticate to the RomM REST API. Session cookies, Basic, O RomM's REST API accepts four authentication modes. Pick the one that matches your client: -| Mode | Who it's for | How the credential is carried | -| -------------------- | -------------------------------------------------------- | ------------------------------------------------------ | -| **Session cookie** | Browser UI | `Cookie: session=…` after `POST /api/auth/login` | -| **HTTP Basic** | Quick scripts, curl one-liners | `Authorization: Basic ` | -| **OAuth2 Bearer** | Automation, CI, third-party apps | `Authorization: Bearer ` | -| **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | +| Mode | Who it's for | How the credential is carried | +| -------------------- | -------------------------------------------------------- | ----------------------------------------------------- | +| **Session cookie** | Browser UI | `Cookie: session=…` after `POST /api/auth/login` | +| **HTTP Basic** | Quick scripts, curl one-liners | `Authorization: Basic ` | +| **OAuth2 Bearer** | Automation, CI, third-party apps | `Authorization: Bearer ` | +| **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | | **OIDC session** | Users who sign in via SSO | Same as session cookie but issued after OIDC callback | All of them resolve to the same scope model. See the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. @@ -130,7 +130,7 @@ A token that holds `users.write` also implicitly grants lesser scopes like `user | HTTP | Meaning | | ------------------ | ----------------------------------------------------------------------- | | `401 Unauthorized` | No credential, expired credential, bad credential. | -| `403 Forbidden` | Authenticated but the identity lacks a required scope. | +| `403 Forbidden` | Authenticated but the identity lacks a required scope. | | `404 Not Found` | The resource doesn't exist, or, for privacy, the identity can't see it. | When debugging a 403, check: diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md index e1321988..c9275023 100644 --- a/docs/developers/api-reference.md +++ b/docs/developers/api-reference.md @@ -11,8 +11,8 @@ RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of tru Every RomM instance hosts two renderings of its own spec: -- **Swagger UI** at `{romm_url}/api/docs`: explore + try endpoints inline. -- **ReDoc** at `{romm_url}/api/redoc`: cleaner reading layout. +- **Swagger UI** at `{romm_url}/api/docs`: explore + try endpoints inline +- **ReDoc** at `{romm_url}/api/redoc`: cleaner reading layout The raw spec: @@ -32,11 +32,11 @@ For code generation, Postman imports, and schema-validation libraries, see [Cons Every write endpoint (and most read endpoints) requires a credential. Five modes supported: -- **Session cookie:** from the web UI. -- **HTTP Basic:** username + password header. -- **OAuth2 Bearer:** JWT access token. -- **Client API Token:** long-lived `rmm_...` bearer token. -- **OIDC session:** after IdP callback, same as a regular session. +- **Session cookie:** from the web UI +- **HTTP Basic:** username + password header +- **OAuth2 Bearer:** JWT access token +- **Client API Token:** long-lived `rmm_...` bearer token +- **OIDC session:** after IdP callback, same as a regular session Full walkthrough in [API Authentication](api-authentication.md). @@ -46,133 +46,133 @@ Every endpoint belongs to a group. Summaries below. The interactive docs at `/ap ### Auth & users -- `POST /api/auth/login`, `/api/auth/logout`: session login/logout. -- `POST /api/token`: OAuth2 token endpoint (password + refresh grants). -- `GET /api/auth/openid`, `/api/oauth/openid`: OIDC flow. -- `POST /api/auth/forgot-password`, `/api/auth/reset-password`: password reset. +- `POST /api/auth/login`, `/api/auth/logout`: session login/logout +- `POST /api/token`: OAuth2 token endpoint (password + refresh grants) +- `GET /api/auth/openid`, `/api/oauth/openid`: OIDC flow +- `POST /api/auth/forgot-password`, `/api/auth/reset-password`: password reset - `GET/POST/PUT/DELETE /api/users`: user management. See [Users & Roles](../administration/users-and-roles.md). -- `POST /api/users/register`: claim an invite link. -- `POST /api/users/invite-link`: generate one. +- `POST /api/users/register`: claim an invite link +- `POST /api/users/invite-link`: generate one ### ROMs -- `GET /api/roms`: list with filters, pagination, search. -- `GET /api/roms/{id}`: single ROM details. -- `POST /api/roms/upload/{start,chunk,complete,cancel}`: chunked upload. -- `PUT /api/roms/{id}`: update metadata. -- `PUT /api/roms/{id}/props`: update per-user data (rating, status). -- `POST /api/roms/delete`: bulk delete. -- `GET /api/roms/{id}/content/{filename}`: download. -- `GET /api/roms/download?ids=...`: bulk zip download. -- `GET /api/roms/by-hash`, `/api/roms/by-metadata-provider`: lookup helpers. +- `GET /api/roms`: list with filters, pagination, search +- `GET /api/roms/{id}`: single ROM details +- `POST /api/roms/upload/{start,chunk,complete,cancel}`: chunked upload +- `PUT /api/roms/{id}`: update metadata +- `PUT /api/roms/{id}/props`: update per-user data (rating, status) +- `POST /api/roms/delete`: bulk delete +- `GET /api/roms/{id}/content/{filename}`: download +- `GET /api/roms/download?ids=...`: bulk zip download +- `GET /api/roms/by-hash`, `/api/roms/by-metadata-provider`: lookup helpers ### ROM notes, files, manuals -- `GET/POST/PUT/DELETE /api/roms/{id}/notes`: per-ROM notes (optional public). -- `GET /api/roms/{id}/files/content/{filename}`: individual file in a multi-file ROM. -- `POST/DELETE /api/roms/{id}/manuals`: PDF manual management. +- `GET/POST/PUT/DELETE /api/roms/{id}/notes`: per-ROM notes (optional public) +- `GET /api/roms/{id}/files/content/{filename}`: individual file in a multi-file ROM +- `POST/DELETE /api/roms/{id}/manuals`: PDF manual management ### Platforms -- `GET /api/platforms`: list. -- `GET /api/platforms/supported`: everything RomM recognises (same data as [Supported Platforms](../platforms/supported-platforms.md)). -- `PUT/DELETE /api/platforms/{id}`: edit or delete. +- `GET /api/platforms`: list +- `GET /api/platforms/supported`: everything RomM recognises (same data as [Supported Platforms](../platforms/supported-platforms.md)) +- `PUT/DELETE /api/platforms/{id}`: edit or delete ### Collections -- `GET/POST/PUT/DELETE /api/collections`: standard collections. -- `POST/DELETE /api/collections/{id}/roms`: add/remove ROMs. -- `GET/POST/PUT/DELETE /api/collections/smart`: [Smart Collections](../using/smart-collections.md). -- `GET /api/collections/virtual`: read-only [Virtual Collections](../using/virtual-collections.md). +- `GET/POST/PUT/DELETE /api/collections`: standard collections +- `POST/DELETE /api/collections/{id}/roms`: add/remove ROMs +- `GET/POST/PUT/DELETE /api/collections/smart`: [Smart Collections](../using/smart-collections.md) +- `GET /api/collections/virtual`: read-only [Virtual Collections](../using/virtual-collections.md) ### Assets: saves, states, screenshots - `GET/POST/PUT /api/saves`: save files. See [Saves & States](../using/saves-and-states.md). -- `POST /api/saves/delete`: bulk delete. -- `GET/POST/PUT /api/states`: emulator states. -- `POST /api/screenshots`: screenshot upload. +- `POST /api/saves/delete`: bulk delete +- `GET/POST/PUT /api/states`: emulator states +- `POST /api/screenshots`: screenshot upload ### Firmware - `GET/POST/DELETE /api/firmware`: firmware management. See [Firmware Management](../administration/firmware-management.md). -- `GET /api/firmware/{id}/content/{filename}`: download. +- `GET /api/firmware/{id}/content/{filename}`: download ### Devices & sync -- `GET/POST/PUT/DELETE /api/devices`: registered device management. -- `POST /api/sync/negotiate`: sync-session negotiation. -- `POST /api/sync/sessions/{id}/complete`: close a sync session with ingested play sessions. -- `POST /api/devices/{id}/push-pull`: trigger manual sync. +- `GET/POST/PUT/DELETE /api/devices`: registered device management +- `POST /api/sync/negotiate`: sync-session negotiation +- `POST /api/sync/sessions/{id}/complete`: close a sync session with ingested play sessions +- `POST /api/devices/{id}/push-pull`: trigger manual sync See [Device Sync Protocol](../ecosystem/device-sync-protocol.md) for the wire-level walkthrough. ### Play sessions -- `GET/POST/DELETE /api/play-sessions`: ingest and query play sessions (up to 100 per POST). +- `GET/POST/DELETE /api/play-sessions`: ingest and query play sessions (up to 100 per POST) ### Client API tokens -- `GET/POST/DELETE /api/client-tokens`: user's tokens. -- `PUT /api/client-tokens/{id}/regenerate`: regenerate the secret. -- `POST /api/client-tokens/{id}/pair`: generate a pairing code. -- `POST /api/client-tokens/exchange`: exchange a pairing code for a token. -- `GET /api/client-tokens/pair/{code}/status`: poll pairing status. -- `GET /api/client-tokens/all`, `DELETE /api/client-tokens/{id}/admin`: admin-only. +- `GET/POST/DELETE /api/client-tokens`: user's tokens +- `PUT /api/client-tokens/{id}/regenerate`: regenerate the secret +- `POST /api/client-tokens/{id}/pair`: generate a pairing code +- `POST /api/client-tokens/exchange`: exchange a pairing code for a token +- `GET /api/client-tokens/pair/{code}/status`: poll pairing status +- `GET /api/client-tokens/all`, `DELETE /api/client-tokens/{id}/admin`: admin-only See [Client API Tokens](../ecosystem/client-api-tokens.md) for the full flow. ### Search -- `GET /api/search/roms`: search across metadata providers (IGDB, Moby, SS, Flashpoint, LaunchBox). -- `GET /api/search/cover`: alternate cover search via SteamGridDB. +- `GET /api/search/roms`: search across metadata providers (IGDB, Moby, SS, Flashpoint, LaunchBox) +- `GET /api/search/cover`: alternate cover search via SteamGridDB ### Tasks -- `GET /api/tasks`, `/api/tasks/status`: what's registered, what's running. -- `POST /api/tasks/run/{task_name}`: trigger on demand (requires `tasks.run`). +- `GET /api/tasks`, `/api/tasks/status`: what's registered, what's running +- `POST /api/tasks/run/{task_name}`: trigger on demand (requires `tasks.run`) ### Configuration -- `GET /api/config`: current config (parts of it public, parts require auth). -- `POST/DELETE /api/config/system/platforms`: add/remove platform bindings. -- `POST/DELETE /api/config/system/versions`: add/remove version mappings. -- `POST/DELETE /api/config/exclude`: add/remove exclusion rules. +- `GET /api/config`: current config (parts of it public, parts require auth) +- `POST/DELETE /api/config/system/platforms`: add/remove platform bindings +- `POST/DELETE /api/config/system/versions`: add/remove version mappings +- `POST/DELETE /api/config/exclude`: add/remove exclusion rules ### Stats -- `GET /api/stats`: aggregate counts + disk usage. -- `GET /api/stats?include_platform_stats=true`: per-platform breakdown. +- `GET /api/stats`: aggregate counts + disk usage +- `GET /api/stats?include_platform_stats=true`: per-platform breakdown ### Feeds -- `GET /api/feeds/webrcade`: WebRcade. -- `GET /api/feeds/tinfoil`: Nintendo Switch. -- `GET /api/feeds/pkgi/{psvita|psp}/{game|dlc}`: PS Vita / PSP. -- `GET /api/feeds/fpkgi/{ps4|ps5}`: PS4 / PS5. -- `GET /api/feeds/kekatsu/{nds}`: Nintendo DS. -- `GET /api/feeds/pkgj/{psx|psvita|psp}`: legacy pkgj. +- `GET /api/feeds/webrcade`: WebRcade +- `GET /api/feeds/tinfoil`: Nintendo Switch +- `GET /api/feeds/pkgi/{psvita|psp}/{game|dlc}`: PS Vita / PSP +- `GET /api/feeds/fpkgi/{ps4|ps5}`: PS4 / PS5 +- `GET /api/feeds/kekatsu/{nds}`: Nintendo DS +- `GET /api/feeds/pkgj/{psx|psvita|psp}`: legacy pkgj See [Feeds](../reference/feeds.md) for format details per feed. ### Exports -- `POST /api/export/gamelist-xml`: ES-DE / Batocera format. -- `POST /api/export/pegasus`: Pegasus frontend format. +- `POST /api/export/gamelist-xml`: ES-DE / Batocera format +- `POST /api/export/pegasus`: Pegasus frontend format See [Exports](../reference/exports.md). ### Heartbeat -- `GET /api/heartbeat`: health + config snapshot. Safe to scrape from uptime monitors. -- `GET /api/heartbeat/metadata/{provider}`: per-provider health. +- `GET /api/heartbeat`: health + config snapshot. Safe to scrape from uptime monitors +- `GET /api/heartbeat/metadata/{provider}`: per-provider health ### Raw -- `GET /api/raw/assets/{path}`: direct asset passthrough for advanced integrations. +- `GET /api/raw/assets/{path}`: direct asset passthrough for advanced integrations ### Netplay -- `GET /api/netplay/list?game_id=...`: active Netplay rooms for a game. +- `GET /api/netplay/list?game_id=...`: active Netplay rooms for a game - WebSocket at `/netplay/socket.io`: room coordination. See [WebSockets](websockets.md). ## WebSockets @@ -183,7 +183,7 @@ REST isn't the only surface. Two Socket.IO endpoints cover live-update and coord RomM's API follows SemVer along with the rest of RomM: -- **Breaking changes only in major versions.** Endpoint removal, required-parameter changes, incompatible response-schema shifts. +- **Breaking changes only in major versions.** Endpoint removal, required-parameter changes, incompatible response-schema shifts - **Minor versions add** endpoints, optional parameters, optional response fields. - **Patch versions fix** bugs without schema changes. @@ -191,8 +191,8 @@ For reproducible builds, pin the OpenAPI spec version at the same RomM version y ## See also -- [API Authentication](api-authentication.md): auth modes in detail. -- [Consuming OpenAPI](openapi.md): codegen + schema validation. -- [WebSockets](websockets.md): Socket.IO endpoints. -- [Client API Tokens](../ecosystem/client-api-tokens.md): recommended companion-app auth. -- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): sync endpoints in depth. +- [API Authentication](api-authentication.md): auth modes in detail +- [Consuming OpenAPI](openapi.md): codegen + schema validation +- [WebSockets](websockets.md): Socket.IO endpoints +- [Client API Tokens](../ecosystem/client-api-tokens.md): recommended companion-app auth +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): sync endpoints in depth diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index 898ba1ff..323c997a 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -21,15 +21,15 @@ rommapp/romm Key sub-directories: -- `backend/routers/`: FastAPI route definitions, one file per resource group. -- `backend/handlers/`: business logic (scan engine, metadata providers, auth). -- `backend/tasks/`: RQ job definitions (scheduled + manual + watcher tasks). -- `backend/config/`: Pydantic config schemas for `config.yml`. -- `backend/alembic/`: DB migrations. -- `backend/romm_test/`: test suite. -- `frontend/src/views/`: top-level Vue pages. -- `frontend/src/console/`: Console Mode SPA (separate entry). -- `frontend/src/stores/`: Pinia state stores. +- `backend/routers/`: FastAPI route definitions, one file per resource group +- `backend/handlers/`: business logic (scan engine, metadata providers, auth) +- `backend/tasks/`: RQ job definitions (scheduled + manual + watcher tasks) +- `backend/config/`: Pydantic config schemas for `config.yml` +- `backend/alembic/`: DB migrations +- `backend/romm_test/`: test suite +- `frontend/src/views/`: top-level Vue pages +- `frontend/src/console/`: Console Mode SPA (separate entry) +- `frontend/src/stores/`: Pinia state stores ## Runtime topology @@ -67,10 +67,10 @@ A running RomM container hosts several cooperating processes: - **nginx:** listens on `8080`. Serves static assets (the SPA bundle, EmulatorJS core files, Ruffle, cover images). Proxies API + WebSocket traffic to gunicorn. Streams large downloads via `mod_zip`. - **gunicorn:** the Python WSGI server, running the FastAPI app. Multiple worker processes. `WEB_SERVER_CONCURRENCY` tunes count. -- **FastAPI backend:** routes, handlers, DB access via SQLAlchemy, Valkey for sessions + cache + queue. -- **RQ workers:** separate process(es) that pop jobs off Valkey queues and run them. Scans, metadata syncs, cleanup tasks. -- **Valkey:** in-container by default (full image), externalisable (Redis-compatible). -- **MariaDB / Postgres / MySQL / SQLite:** always external (or a separate container). +- **FastAPI backend:** routes, handlers, DB access via SQLAlchemy, Valkey for sessions + cache + queue +- **RQ workers:** separate process(es) that pop jobs off Valkey queues and run them. Scans, metadata syncs, cleanup tasks +- **Valkey:** in-container by default (full image), externalisable (Redis-compatible) +- **MariaDB / Postgres / MySQL / SQLite:** always external (or a separate container) ## Request lifecycle @@ -110,12 +110,12 @@ Large uploads go through the chunked-upload flow for a reason: gunicorn's per-re ### Main UI (`frontend/src/`) -- **Vue 3 + Composition API**. -- **Vuetify 3** for UI components. -- **Vite** for build + dev server with HMR. -- **Pinia** for state management: one store per resource family (auth, roms, platforms, collections, etc.). -- **vue-i18n** for localisation. Translations in `src/locales//`. -- **socket.io-client** for live updates. +- **Vue 3 + Composition API** +- **Vuetify 3** for UI components +- **Vite** for build + dev server with HMR +- **Pinia** for state management: one store per resource family (auth, roms, platforms, collections, etc.) +- **vue-i18n** for localisation. Translations in `src/locales//` +- **socket.io-client** for live updates Router in `src/plugins/router.ts`. Top-level views in `src/views/`. @@ -123,16 +123,16 @@ Router in `src/plugins/router.ts`. Top-level views in `src/views/`. Separate SPA, compiled as a second bundle. Entry point at `/console`. Built with the same Vue + Pinia + i18n stack but: -- Own router with `/console` namespace. -- Own component library tuned for gamepad navigation (bigger targets, spatial focus, SFX). -- Own theme in `src/console/styles/`. +- Own router with `/console` namespace +- Own component library tuned for gamepad navigation (bigger targets, spatial focus, SFX) +- Own theme in `src/console/styles/` ## Background work RQ has three priority queues: -- **high:** user-triggered scans, manual tasks. Get fast. -- **default:** scheduled nightlies, sync operations. +- **high:** user-triggered scans, manual tasks. Get fast +- **default:** scheduled nightlies, sync operations - **low:** cleanup, image conversion. Run when the system's idle. All queues share the same worker pool. Jobs are persisted to Valkey, so restarting RomM doesn't lose in-flight work (appendonly needs to be on, see [Redis or Valkey](../install/redis-or-valkey.md)). @@ -147,18 +147,18 @@ Client API Tokens are stored as hash-only in the DB (we never store the plaintex ## Observability -- **Sentry** (opt-in): unhandled exceptions. -- **OpenTelemetry** (opt-in): traces, metrics, logs via OTLP. -- **`/api/heartbeat`:** aggregated health snapshot. +- **Sentry** (opt-in): unhandled exceptions +- **OpenTelemetry** (opt-in): traces, metrics, logs via OTLP +- **`/api/heartbeat`:** aggregated health snapshot See [Observability](../administration/observability.md). ## Why these choices -- **FastAPI**: modern, async, auto-OpenAPI. Good fit for the API-shape of the app. -- **Vue + Vuetify**: fast, componenty, looks decent out of the box. -- **MariaDB** as default: solid, familiar, works everywhere. -- **RQ** not Celery: simpler, Redis-native, less operational overhead. Works fine at RomM's scale. +- **FastAPI**: modern, async, auto-OpenAPI. Good fit for the API-shape of the app +- **Vue + Vuetify**: fast, componenty, looks decent out of the box +- **MariaDB** as default: solid, familiar, works everywhere +- **RQ** not Celery: simpler, Redis-native, less operational overhead. Works fine at RomM's scale - **Socket.IO**: pragmatic. Raw WebSocket would be leaner but SIO's client ecosystem is worth the overhead. ## Contributing @@ -169,7 +169,7 @@ For large changes, read the relevant handler in `backend/handlers/` first. The p ## See also -- [Development Setup](development-setup.md): get a local env running. -- [API Reference](api-reference.md): what the backend exposes. -- [WebSockets](websockets.md): Socket.IO endpoints in detail. -- [Configuration File](../reference/configuration-file.md): `config.yml` schema. +- [Development Setup](development-setup.md): get a local env running +- [API Reference](api-reference.md): what the backend exposes +- [WebSockets](websockets.md): Socket.IO endpoints in detail +- [Configuration File](../reference/configuration-file.md): `config.yml` schema diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index 482c5e72..643a9d25 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -38,14 +38,14 @@ Failing to disclose is rude to the humans reviewing your PR. Don't do it. ### Code - **Features:** open an issue first for anything non-trivial. -- **Bug fixes:** happy to take these without pre-discussion if they're small and focused. +- **Bug fixes:** happy to take these without pre-discussion if they're small and focused - **Refactors:** open an issue first. Refactors without a clear user-facing win are usually rejected. ### Documentation You're on the docs site right now. If something's wrong, unclear, or missing: -- PRs welcome against [rommapp/docs](https://github.com/rommapp/docs). +- PRs welcome against [rommapp/docs](https://github.com/rommapp/docs) - Small fixes (typos, broken links) don't need an issue first. - Bigger changes (restructuring, adding a new section): open an issue first or ping in Discord. @@ -67,10 +67,10 @@ See [Translations (i18n)](i18n.md) for the full translator workflow. [Open an issue](https://github.com/rommapp/romm/issues) with: -- What happened, what you expected. -- Exact reproduction steps. -- RomM version and how you deployed it (Docker tag, Unraid, K8s, etc.). -- Relevant logs (`docker logs romm`, redact any secrets). +- What happened, what you expected +- Exact reproduction steps +- RomM version and how you deployed it (Docker tag, Unraid, K8s, etc.) +- Relevant logs (`docker logs romm`, redact any secrets) The bug report template prompts for all of this. diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index 2b0ad23a..f60a75cd 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -197,10 +197,10 @@ uv run pytest -k scan # match by name If you're new to the codebase, read [Architecture](architecture.md) for a high-level walkthrough, then come back here. The short version: -- `backend/`: FastAPI, SQLAlchemy, Alembic, RQ workers. -- `frontend/`: Vue 3 + Vuetify + Pinia + Vite. Separate `/console` SPA for TV/gamepad mode. -- `docker/`: nginx config (with `mod_zip`), entrypoint scripts, multi-stage Dockerfiles. -- `examples/`: reference compose files. +- `backend/`: FastAPI, SQLAlchemy, Alembic, RQ workers +- `frontend/`: Vue 3 + Vuetify + Pinia + Vite. Separate `/console` SPA for TV/gamepad mode +- `docker/`: nginx config (with `mod_zip`), entrypoint scripts, multi-stage Dockerfiles +- `examples/`: reference compose files ## Getting stuck diff --git a/docs/developers/i18n.md b/docs/developers/i18n.md index 817da725..66067c28 100644 --- a/docs/developers/i18n.md +++ b/docs/developers/i18n.md @@ -17,9 +17,9 @@ This page is for **contributors** adding or improving translations. ## Tech stack -- **Library**: [vue-i18n](https://vue-i18n.intlify.dev/). -- **File format**: JSON (one file per locale). -- **Location**: `frontend/src/locales//`. +- **Library**: [vue-i18n](https://vue-i18n.intlify.dev/) +- **File format**: JSON (one file per locale) +- **Location**: `frontend/src/locales//` Each locale folder contains translation files. `en_US/` is the reference, always kept complete and current. Other locales may have partial coverage (missing keys fall back to `en_US`). @@ -82,7 +82,7 @@ Scenario: the language you want isn't in the dropdown yet. Match the source tone: - **Conversational but not too casual.** "Let's scan your library" is fine but "Yo, scan your roms dude" isn't. -- **Active voice.** "Scan the library", not "The library is being scanned". +- **Active voice.** "Scan the library", not "The library is being scanned" - **Consistent formality.** Pick `tu`/`vous`, formal/informal `you`, etc., and stick with it across the locale. ### Consistency @@ -112,9 +112,9 @@ Pipe-separated forms: zero | one | other. Not every language has three, and `vue ### What not to translate -- Proper nouns (RomM, IGDB, ScreenScraper, RetroAchievements). -- Technical identifiers (env var names, config keys, URLs). -- Code samples and filenames. +- Proper nouns (RomM, IGDB, ScreenScraper, RetroAchievements) +- Technical identifiers (env var names, config keys, URLs) +- Code samples and filenames ### RTL languages @@ -139,12 +139,12 @@ We don't use a translation-management platform (Weblate, Crowdin, etc.) in 5.0. ## PR conventions - One PR per locale is ideal. -- Title: `i18n(): `. E.g. `i18n(fr_FR): fill in missing Admin panel strings`. +- Title: `i18n(): `. E.g. `i18n(fr_FR): fill in missing Admin panel strings` - Include screenshots of the changed screens, because reviewers can't read every locale. - Disclose AI assistance per [Contributing](contributing.md). ## See also -- [Languages](../using/languages.md): the end-user view. -- [Contributing](contributing.md): general contribution rules. -- [vue-i18n docs](https://vue-i18n.intlify.dev/): upstream reference. +- [Languages](../using/languages.md): the end-user view +- [Contributing](contributing.md): general contribution rules +- [vue-i18n docs](https://vue-i18n.intlify.dev/): upstream reference diff --git a/docs/developers/index.md b/docs/developers/index.md index 29c1da37..0208313a 100644 --- a/docs/developers/index.md +++ b/docs/developers/index.md @@ -11,33 +11,33 @@ Looking for end-user or operator content? See [Using RomM](../using/index.md) or ## Working with the API -- **[API Reference](api-reference.md):** every REST endpoint. OpenAPI-driven. -- **[API Authentication](api-authentication.md):** all five auth modes (session, Basic, OAuth2, Client API Token, OIDC). -- **[Consuming OpenAPI](openapi.md):** codegen, Postman imports, schema validation. -- **[WebSockets](websockets.md):** Socket.IO endpoints for live updates and Netplay. +- **[API Reference](api-reference.md):** every REST endpoint. OpenAPI-driven +- **[API Authentication](api-authentication.md):** all five auth modes (session, Basic, OAuth2, Client API Token, OIDC) +- **[Consuming OpenAPI](openapi.md):** codegen, Postman imports, schema validation +- **[WebSockets](websockets.md):** Socket.IO endpoints for live updates and Netplay ## Building companion apps -- **[Client API Tokens](../ecosystem/client-api-tokens.md):** how companion apps authenticate, including the device-pairing flow. -- **[Device Sync Protocol](../ecosystem/device-sync-protocol.md):** wire-level reference for save/state/play-session sync. -- **[Argosy](../ecosystem/argosy.md), [Grout](../ecosystem/grout.md):** reference implementations. +- **[Client API Tokens](../ecosystem/client-api-tokens.md):** how companion apps authenticate, including the device-pairing flow +- **[Device Sync Protocol](../ecosystem/device-sync-protocol.md):** wire-level reference for save/state/play-session sync +- **[Argosy](../ecosystem/argosy.md), [Grout](../ecosystem/grout.md):** reference implementations ## Contributing to RomM itself -- **[Development Setup](development-setup.md):** get a local environment running. -- **[Architecture](architecture.md):** high-level walkthrough of the codebase. -- **[Contributing](contributing.md):** process, style, AI-assistance disclosure. -- **[Translations (i18n)](i18n.md):** add or improve a locale. -- **[Releasing](releasing.md):** maintainer-only, how releases are cut. +- **[Development Setup](development-setup.md):** get a local environment running +- **[Architecture](architecture.md):** high-level walkthrough of the codebase +- **[Contributing](contributing.md):** process, style, AI-assistance disclosure +- **[Translations (i18n)](i18n.md):** add or improve a locale +- **[Releasing](releasing.md):** maintainer-only, how releases are cut ## Reference -- **[Environment Variables](../reference/environment-variables.md):** every env var. -- **[Configuration File](../reference/configuration-file.md):** `config.yml` schema. -- **[Scheduled Tasks](../reference/scheduled-tasks.md):** background job reference. -- **[Exports](../reference/exports.md):** gamelist.xml / Pegasus export formats. -- **[Feeds](../reference/feeds.md):** every URL-feed endpoint (Tinfoil, pkgj, WebRcade, etc.). -- **[Glossary](../reference/glossary.md):** canonical terminology. +- **[Environment Variables](../reference/environment-variables.md):** every env var +- **[Configuration File](../reference/configuration-file.md):** `config.yml` schema +- **[Scheduled Tasks](../reference/scheduled-tasks.md):** background job reference +- **[Exports](../reference/exports.md):** gamelist.xml / Pegasus export formats +- **[Feeds](../reference/feeds.md):** every URL-feed endpoint (Tinfoil, pkgj, WebRcade, etc.) +- **[Glossary](../reference/glossary.md):** canonical terminology ## Quick orientation diff --git a/docs/developers/openapi.md b/docs/developers/openapi.md index 44aa3515..a9ab0d9c 100644 --- a/docs/developers/openapi.md +++ b/docs/developers/openapi.md @@ -68,9 +68,9 @@ Generated clients handle auth, request shaping, and response parsing. Drop in, i All three import OpenAPI directly: -- **Postman:** File → Import → paste `openapi.json` URL. -- **Insomnia:** Create → Import From → URL. -- **Bruno:** Import Collection → OpenAPI. +- **Postman:** File → Import → paste `openapi.json` URL +- **Insomnia:** Create → Import From → URL +- **Bruno:** Import Collection → OpenAPI Useful for manual API exploration during development. @@ -78,9 +78,9 @@ Useful for manual API exploration during development. If you're building something that calls RomM, consider validating requests against the spec before sending. Schema-driven validation catches bugs early: -- **Python:** [`openapi-core`](https://github.com/python-openapi/openapi-core). -- **Node.js:** [`openapi-backend`](https://github.com/anttiviljami/openapi-backend) or `ajv`-based approaches. -- **Go:** [`kin-openapi`](https://github.com/getkin/kin-openapi). +- **Python:** [`openapi-core`](https://github.com/python-openapi/openapi-core) +- **Node.js:** [`openapi-backend`](https://github.com/anttiviljami/openapi-backend) or `ajv`-based approaches +- **Go:** [`kin-openapi`](https://github.com/getkin/kin-openapi) ## Spec quirks @@ -96,6 +96,6 @@ Not currently in the spec. Event-driven integration is via [WebSockets](websocke ## See also -- [API Reference](api-reference.md): the pre-rendered version of the spec for browsing. -- [API Authentication](api-authentication.md): required auth for most endpoints. -- [OpenAPI Initiative](https://www.openapis.org/): upstream specification. +- [API Reference](api-reference.md): the pre-rendered version of the spec for browsing +- [API Authentication](api-authentication.md): required auth for most endpoints +- [OpenAPI Initiative](https://www.openapis.org/): upstream specification diff --git a/docs/developers/releasing.md b/docs/developers/releasing.md index 9377c074..60ad312c 100644 --- a/docs/developers/releasing.md +++ b/docs/developers/releasing.md @@ -13,15 +13,15 @@ RomM releases on a loose cadence, not scheduled, driven by readiness: - **Patch (`5.0.1`, `5.0.2`):** bug fixes. Cut as needed, typically 1-4 per month. - **Minor (`5.1.0`, `5.2.0`):** additive features. Cut when a cohesive batch of features is stable. -- **Major (`6.0.0`):** breaking changes. Planned well in advance, announced in the Discord + on GitHub. +- **Major (`6.0.0`):** breaking changes. Planned well in advance, announced in the Discord + on GitHub ## Version numbering [SemVer](https://semver.org/) for breaking-change semantics: -- **MAJOR:** backwards-incompatible schema change, env var rename, or API-contract break. -- **MINOR:** new feature, backwards-compatible. -- **PATCH:** bug fix only. +- **MAJOR:** backwards-incompatible schema change, env var rename, or API-contract break +- **MINOR:** new feature, backwards-compatible +- **PATCH:** bug fix only Alembic migrations run on every startup, and migrations are backwards-compatible within a major version. @@ -29,16 +29,16 @@ Alembic migrations run on every startup, and migrations are backwards-compatible ### 1. Merge pending PRs -- All release-milestoned PRs merged into `master`. -- CI green on `master`. -- Linter green: `trunk check --all`. -- Tests green: `uv run pytest`. +- All release-milestoned PRs merged into `master` +- CI green on `master` +- Linter green: `trunk check --all` +- Tests green: `uv run pytest` ### 2. Update version numbers -- `pyproject.toml` → `version = "X.Y.Z"`. -- `frontend/package.json` → `"version": "X.Y.Z"`. -- Any hardcoded version strings (`backend/__init__.py`, etc.). `rg '__version__'` or `rg '5\.0\.0'` to find them. +- `pyproject.toml` → `version = "X.Y.Z"` +- `frontend/package.json` → `"version": "X.Y.Z"` +- Any hardcoded version strings (`backend/__init__.py`, etc.). `rg '__version__'` or `rg '5\.0\.0'` to find them ### 3. Update `env.template` if needed @@ -111,7 +111,7 @@ docker push rommapp/romm:5 ## Announcements - **Discord `#announcements`:** post a short summary with a link to the GitHub Release. -- **Reddit** (r/selfhosted, etc.): optional, for major versions. +- **Reddit** (r/selfhosted, etc.): optional, for major versions - **Docs site version switcher:** publish a new `mike`-managed version of the docs. See [Docs versioning](#docs-versioning). ## Docs versioning @@ -162,6 +162,6 @@ For major versions: ## See also -- [Release Notes & Migration](../releases/index.md): user-facing side. -- [Upgrading to 5.0](../releases/upgrading-to-5.0.md): reference migration guide style. -- [Contributing](contributing.md): general contribution process. +- [Release Notes & Migration](../releases/index.md): user-facing side +- [Upgrading to 5.0](../releases/upgrading-to-5.0.md): reference migration guide style +- [Contributing](contributing.md): general contribution process diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md index 50c51cf5..5ced6227 100644 --- a/docs/developers/websockets.md +++ b/docs/developers/websockets.md @@ -93,8 +93,8 @@ When you run multiple RomM replicas behind a load balancer, a WS-originated even Required config when running multi-replica: -- `REDIS_HOST` + `REDIS_PORT` shared across replicas. -- Load balancer with **sticky sessions** (client IP or cookie hash). +- `REDIS_HOST` + `REDIS_PORT` shared across replicas +- Load balancer with **sticky sessions** (client IP or cookie hash) Without sticky sessions, Socket.IO's handshake polling phase can bounce between replicas and fail. See [Socket.IO multi-server docs](https://socket.io/docs/v4/using-multiple-nodes/) for context. @@ -104,9 +104,9 @@ Every reverse-proxy setup must forward the WebSocket upgrade. See [Reverse Proxy Common breakages: -- Nginx without `proxy_set_header Upgrade $http_upgrade` / `Connection "upgrade"`. -- Cloudflare with WebSockets off in Network settings. -- Traefik without the default passthrough middlewares. +- Nginx without `proxy_set_header Upgrade $http_upgrade` / `Connection "upgrade"` +- Cloudflare with WebSockets off in Network settings +- Traefik without the default passthrough middlewares Symptom of a broken WS: HTTP 400 responses on the upgrade, and the browser console full of `WebSocket connection failed`. See [Authentication Troubleshooting → WebSockets](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). @@ -143,6 +143,6 @@ sio.wait() ## See also -- [API Authentication](api-authentication.md): general auth primer. +- [API Authentication](api-authentication.md): general auth primer - [Reverse Proxy](../install/reverse-proxy.md): every recipe needs WebSocket passthrough. -- [Netplay](../using/netplay.md): end-user-facing side of the `/netplay` endpoint. +- [Netplay](../using/netplay.md): end-user-facing side of the `/netplay` endpoint diff --git a/docs/ecosystem/argosy.md b/docs/ecosystem/argosy.md index 6be2b359..a8f079f6 100644 --- a/docs/ecosystem/argosy.md +++ b/docs/ecosystem/argosy.md @@ -14,11 +14,11 @@ description: Official Android launcher for RomM, browse and launch your library ## What it does -- Browses your full RomM library from an Android device. -- Handles authentication via [Client API Tokens](client-api-tokens.md), paired over a short code so you don't type a 44-character string. -- Downloads ROMs on demand to your device's storage. -- Launches RetroArch (or another configured emulator) with the downloaded ROM. -- Syncs saves / states back to RomM when you finish a session (optional). +- Browses your full RomM library from an Android device +- Handles authentication via [Client API Tokens](client-api-tokens.md), paired over a short code so you don't type a 44-character string +- Downloads ROMs on demand to your device's storage +- Launches RetroArch (or another configured emulator) with the downloaded ROM +- Syncs saves / states back to RomM when you finish a session (optional) ## Installing @@ -37,7 +37,7 @@ Not currently on either, so APK sideloading is the path for now. 2. Enter your RomM URL (e.g. `https://romm.example.com`), which needs HTTPS in production. 3. Choose **Pair Device**. 4. Argosy shows a pairing URL or QR code: open it on a device that's already signed into RomM. -5. On that device, RomM shows a confirmation dialog; enter the 8-digit code Argosy displayed. +5. Enter the 8-digit code Argosy displayed on that RomM page. 6. Accept, and Argosy receives a Client API Token bound to your RomM account. From here, Argosy lists your library. Full pairing-flow details in [Client API Tokens](client-api-tokens.md). @@ -65,7 +65,7 @@ RetroArch users: Argosy can auto-detect installed RetroArch cores and map platfo Argosy → Settings → Sync. Two modes: -- **On session end**: uploads saves back to RomM when you exit the emulator. +- **On session end**: uploads saves back to RomM when you exit the emulator - **Manual**: you tap Upload when you want. Saves show up in RomM's **Game Data** tab on the game, same as in-browser saves: see [Saves & States](../using/saves-and-states.md). @@ -74,9 +74,9 @@ Saves show up in RomM's **Game Data** tab on the game, same as in-browser saves: Argosy needs: -- **Storage**: to save downloaded ROMs. -- **Network**: to talk to your RomM instance. -- **Optional: Notifications**: download-complete and sync-complete pings. +- **Storage**: to save downloaded ROMs +- **Network**: to talk to your RomM instance +- **Optional: Notifications**: download-complete and sync-complete pings No other permissions: the app doesn't request contacts, camera, location, or anything else. @@ -84,13 +84,13 @@ No other permissions: the app doesn't request contacts, camera, location, or any - **Can't connect to RomM**: check the URL (including `https://`) and that the RomM instance is reachable from your mobile network. Cellular might be blocked, so try Wi-Fi first. - **Token invalid**: pair again, since tokens can expire or be revoked on the RomM side. -- **Emulator won't launch**: make sure the emulator app is installed and Argosy has permission to open it; some emulators require an intent-filter setup. +- **Emulator won't launch**: make sure the emulator app is installed and Argosy has permission to open it, as some emulators require an intent-filter setup. - **Downloads fail partway**: usually network, and Argosy resumes on retry. Full sync-specific debugging in [Device Sync Troubleshooting](../troubleshooting/sync.md). ## See also -- [Client API Tokens](client-api-tokens.md): the auth + pairing flow Argosy uses. -- [Device Sync Protocol](device-sync-protocol.md): how saves sync. -- [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher): source, issues, releases. +- [Client API Tokens](client-api-tokens.md): the auth + pairing flow Argosy uses +- [Device Sync Protocol](device-sync-protocol.md): how saves sync +- [rommapp/argosy-launcher](https://github.com/rommapp/argosy-launcher): source, issues, releases diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index 8f758329..89b65b63 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -34,9 +34,9 @@ Authorization: Bearer rmm_abcdef... From the RomM UI: **Profile → Client API Tokens → + New Token**. -- **Name**: descriptive (e.g. "Grout on RG35XX"). +- **Name**: descriptive (e.g. "Grout on RG35XX") - **Scopes**: tick which scopes to include. Default: read-only. Think about it, and don't give every token `users.write`. -- **Expiry**: optional, blank = never expires until revoked. +- **Expiry**: optional, blank = never expires until revoked The token is shown **exactly once**. Copy it now. If you lose it, revoke and regenerate, because you can't get it back. @@ -149,10 +149,10 @@ A token can only hold scopes the owning user _also_ holds. If the user is an Edi Useful narrow scope-sets: -- **Library-only read**: `roms.read`, `platforms.read`, `collections.read`, `devices.read` (e.g. for a browse-only app). +- **Library-only read**: `roms.read`, `platforms.read`, `collections.read`, `devices.read` (e.g. for a browse-only app) - **Read + sync saves**: add `assets.read`, `assets.write`, `me.read`, `me.write`, `devices.write`. -- **Playnite / external launcher**: `roms.read`, `platforms.read`, `collections.read` (it only needs to browse and download). -- **Grout / handheld companion**: library-read + assets read/write + device management. +- **Playnite / external launcher**: `roms.read`, `platforms.read`, `collections.read` (it only needs to browse and download) +- **Grout / handheld companion**: library-read + assets read/write + device management The UI's scope-selection step defaults to read-only. Only tick write scopes you actually need. @@ -174,6 +174,6 @@ If the user is deleted, all their tokens are revoked immediately. ## See also -- [Device Sync Protocol](device-sync-protocol.md): how the synced content flows after pairing. -- [API Authentication](../developers/api-authentication.md): all RomM auth modes side-by-side. -- [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix): the 19-scope taxonomy. +- [Device Sync Protocol](device-sync-protocol.md): how the synced content flows after pairing +- [API Authentication](../developers/api-authentication.md): all RomM auth modes side-by-side +- [Users & Roles → scope matrix](../administration/users-and-roles.md#scope-matrix): the 19-scope taxonomy diff --git a/docs/ecosystem/community-apps.md b/docs/ecosystem/community-apps.md index 9ddaaafb..af2871d4 100644 --- a/docs/ecosystem/community-apps.md +++ b/docs/ecosystem/community-apps.md @@ -9,10 +9,10 @@ Apps listed here are **community-maintained**. The RomM team doesn't build or of First-party alternatives (built by the RomM team): -- [Argosy Launcher](argosy.md) (Android). -- [Grout](grout.md) (Linux handhelds). -- [Playnite Plugin](playnite-plugin.md) (Windows). -- [muOS App](muos-app.md) (muOS handhelds). +- [Argosy Launcher](argosy.md) (Android) +- [Grout](grout.md) (Linux handhelds) +- [Playnite Plugin](playnite-plugin.md) (Windows) +- [muOS App](muos-app.md) (muOS handhelds) ## Mobile @@ -121,11 +121,11 @@ Push a Syncthing-managed library to RomM automatically. Built something? Open a PR on [rommapp/docs](https://github.com/rommapp/docs) adding your project to this list with: -- Name, short description. -- Author GitHub handle. -- Platform(s). -- Repo link. -- Status (Active / Maintenance-mode / Abandoned). +- Name, short description +- Author GitHub handle +- Platform(s) +- Repo link +- Status (Active / Maintenance-mode / Abandoned) We'll merge if the project's real, has a working repo, and isn't obviously broken. diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md index 1bd20441..e309ddae 100644 --- a/docs/ecosystem/device-sync-protocol.md +++ b/docs/ecosystem/device-sync-protocol.md @@ -29,7 +29,7 @@ Required scopes: | ---------------------------- | ------------------------------------------------------------- | | `/devices/*` | `devices.read`, `devices.write` | | `/sync/*` | `assets.read`, `assets.write`, `devices.write` | -| `/play-sessions/*` | `me.read`, `me.write` (read own); `users.read` (read others') | +| `/play-sessions/*` | `me.read`, `me.write` (read own), `users.read` (read others') | | `/assets/*` (save/state I/O) | `assets.read`, `assets.write` | ## Registering a device @@ -151,7 +151,7 @@ RomM returns a set of **operations** the device should execute: - `keep_both`: rename and keep both copies. - `server_wins` / `device_wins`: overwrite the other. - Default is `keep_both`. -- **`noop`**: nothing to do because hashes match. +- **`noop`**: nothing to do because hashes match ### Execution @@ -239,8 +239,8 @@ Currently polling-only. Companion apps check `/api/sync/negotiate` periodically ## See also -- [Client API Tokens](client-api-tokens.md): auth + pairing. -- [API Authentication](../developers/api-authentication.md): general auth primer. -- [API Reference](../developers/api-reference.md): full endpoint catalogue. -- [SSH Sync](../administration/ssh-sync.md): alternative transport for handhelds. -- [Argosy](argosy.md), [Grout](grout.md): reference client implementations. +- [Client API Tokens](client-api-tokens.md): auth + pairing +- [API Authentication](../developers/api-authentication.md): general auth primer +- [API Reference](../developers/api-reference.md): full endpoint catalogue +- [SSH Sync](../administration/ssh-sync.md): alternative transport for handhelds +- [Argosy](argosy.md), [Grout](grout.md): reference client implementations diff --git a/docs/ecosystem/fpkgi.md b/docs/ecosystem/fpkgi.md index e5f00889..fe51230d 100644 --- a/docs/ecosystem/fpkgi.md +++ b/docs/ecosystem/fpkgi.md @@ -11,8 +11,8 @@ New in RomM 5.0. Earlier versions didn't have fpkgi feeds. ## Prerequisites -- **PS4 or PS5** with fpkgi installed (requires CFW / jailbreak, and setup is out of scope here). -- **RomM reachable from the console over Wi-Fi.** LAN simplest. +- **PS4 or PS5** with fpkgi installed (requires CFW / jailbreak, and setup is out of scope here) +- **RomM reachable from the console over Wi-Fi.** LAN simplest - Games stored as `.pkg` files. fpkgi, like pkgj, only handles the Sony installer format. ## Feed URL @@ -23,8 +23,8 @@ New in RomM 5.0. Earlier versions didn't have fpkgi feeds. Where `{platform_slug}` is: -- `ps4` for PlayStation 4 content. -- `ps5` for PlayStation 5 content. +- `ps4` for PlayStation 4 content +- `ps5` for PlayStation 5 content Example: @@ -59,10 +59,10 @@ PS4 `.pkg` files specifically, not `.iso`, not compressed. RomM filters to `.pkg - **Feed is empty.** No `.pkg` files on the `ps4` / `ps5` platform. Check your library. - **Downloads fail with 401.** Auth config mismatch. See Authentication section above. -- **Downloads succeed but install fails.** `.pkg` is for a different firmware version. Not a RomM problem. +- **Downloads succeed but install fails.** `.pkg` is for a different firmware version. Not a RomM problem ## See also -- [Feeds reference](../reference/feeds.md): all feed endpoints. -- [pkgj](pkgj.md): PS Vita / PSP equivalent. -- [fpkgi upstream](https://github.com/CyberYoshi64/fpkgi): installer homebrew. +- [Feeds reference](../reference/feeds.md): all feed endpoints +- [pkgj](pkgj.md): PS Vita / PSP equivalent +- [fpkgi upstream](https://github.com/CyberYoshi64/fpkgi): installer homebrew diff --git a/docs/ecosystem/grout.md b/docs/ecosystem/grout.md index 511fb89f..b2213e01 100644 --- a/docs/ecosystem/grout.md +++ b/docs/ecosystem/grout.md @@ -14,15 +14,15 @@ description: Official Linux handheld companion for muOS and NextUI, sync ROMs an ## What it does -- Connects to your RomM instance using a [Client API Token](client-api-tokens.md). -- **Pulls** ROMs from RomM to the handheld's SD card, organised into muOS / NextUI's expected folder layout. -- **Pushes** saves and states back to RomM when you finish a session. -- **Schedules** sync runs: on idle, on session end, or on a cron. -- Works fully offline between syncs, so the handheld doesn't need RomM to play. +- Connects to your RomM instance using a [Client API Token](client-api-tokens.md) +- **Pulls** ROMs from RomM to the handheld's SD card, organised into muOS / NextUI's expected folder layout +- **Pushes** saves and states back to RomM when you finish a session +- **Schedules** sync runs: on idle, on session end, or on a cron +- Works fully offline between syncs, so the handheld doesn't need RomM to play ## Why Grout (and not Argosy)? -- **[Argosy](argosy.md)** is Android-native. Good for Android handhelds (Retroid Pocket running Android, phones). +- **[Argosy](argosy.md)** is Android-native. Good for Android handhelds (Retroid Pocket running Android, phones) - **Grout** is for non-Android Linux handhelds. muOS and NextUI are Linux, not Android. Argosy's APK won't install. Same underlying protocol but different client for different OS. @@ -68,24 +68,24 @@ Bulk select for multi-file downloads, useful for pulling a whole collection at o Grout → Settings → Sync: -- **Pull** cadence: how often to check RomM for new ROMs (default: manual, and can be set to every N minutes when on Wi-Fi). -- **Push** cadence: how often to upload saves (default: on session end). -- **Full sync**: manual. Triggers a full bidirectional sync right now. +- **Pull** cadence: how often to check RomM for new ROMs (default: manual, and can be set to every N minutes when on Wi-Fi) +- **Push** cadence: how often to upload saves (default: on session end) +- **Full sync**: manual. Triggers a full bidirectional sync right now ### What pushes back to RomM - **Save files.** Once a session ends, Grout uploads any changed saves to RomM. -- **Save states.** Same, if enabled in settings. -- **Play session records.** Start/end times for the **Continue Playing** ribbon and per-ROM playtime on RomM. +- **Save states.** Same, if enabled in settings +- **Play session records.** Start/end times for the **Continue Playing** ribbon and per-ROM playtime on RomM ## Permissions and access Grout uses your Client API Token for all API calls. Token scopes: -- `roms.read`, `platforms.read`, `collections.read`: to browse. -- `assets.read`, `assets.write`: to sync saves. -- `devices.read`, `devices.write`: to register as a device. -- `firmware.read`: if you're syncing firmware too. +- `roms.read`, `platforms.read`, `collections.read`: to browse +- `assets.read`, `assets.write`: to sync saves +- `devices.read`, `devices.write`: to register as a device +- `firmware.read`: if you're syncing firmware too Scope the token narrowly when creating: default scopes are fine for most users but an admin shouldn't hand Grout `users.write` just because the token-creation page offers it. @@ -104,7 +104,7 @@ More in [Device Sync Troubleshooting](../troubleshooting/sync.md). ## See also -- [Client API Tokens](client-api-tokens.md): token and pairing flow reference. -- [Device Sync Protocol](device-sync-protocol.md): wire-level protocol. -- [SSH Sync](../administration/ssh-sync.md): operator-side SSH config. -- [rommapp/grout](https://github.com/rommapp/grout): source, issues, releases. +- [Client API Tokens](client-api-tokens.md): token and pairing flow reference +- [Device Sync Protocol](device-sync-protocol.md): wire-level protocol +- [SSH Sync](../administration/ssh-sync.md): operator-side SSH config +- [rommapp/grout](https://github.com/rommapp/grout): source, issues, releases diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md index 03e37e7a..96bb3355 100644 --- a/docs/ecosystem/igir.md +++ b/docs/ecosystem/igir.md @@ -40,8 +40,8 @@ cp -r roms/ roms-unverified/ DAT files are hash-referenced catalogues Igir matches against. -- **Cartridge systems:** [No-Intro daily](https://datomatic.no-intro.org/index.php?page=download&op=daily), full DAT compilation. -- **Optical systems (PS1, Saturn, etc.):** [Redump](http://redump.org/downloads/), per-platform DAT files. +- **Cartridge systems:** [No-Intro daily](https://datomatic.no-intro.org/index.php?page=download&op=daily), full DAT compilation +- **Optical systems (PS1, Saturn, etc.):** [Redump](http://redump.org/downloads/), per-platform DAT files Drop the DAT files into `dats/`. You can use a subset if you only care about specific platforms. @@ -96,9 +96,9 @@ chmod +x igir-romm-cleanup.sh Output: -- `roms-verified/{platform-slug}/{Proper Game Name}.rom`: identified ROMs in RomM layout. -- `roms-unverified/`: whatever Igir didn't identify, still available for manual review. -- `report.csv` (or similar): what matched, what didn't. +- `roms-verified/{platform-slug}/{Proper Game Name}.rom`: identified ROMs in RomM layout +- `roms-unverified/`: whatever Igir didn't identify, still available for manual review +- `report.csv` (or similar): what matched, what didn't ## Manually migrate leftovers @@ -168,6 +168,6 @@ Run a scan from RomM. Everything should match cleanly against providers. ## See also -- [Igir docs](https://igir.io/): the upstream reference. -- [Folder Structure](../getting-started/folder-structure.md): what RomM expects on-disk. -- [Metadata Providers](../administration/metadata-providers.md): how RomM matches after Igir's done its work. +- [Igir docs](https://igir.io/): the upstream reference +- [Folder Structure](../getting-started/folder-structure.md): what RomM expects on-disk +- [Metadata Providers](../administration/metadata-providers.md): how RomM matches after Igir's done its work diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md index 6b93e4bb..f01f4d55 100644 --- a/docs/ecosystem/index.md +++ b/docs/ecosystem/index.md @@ -11,20 +11,20 @@ RomM has a sizeable ecosystem of companion apps and integration patterns. This h Maintained by the RomM team. -- **[Argosy Launcher](argosy.md)**: Android launcher that browses and launches your RomM library on mobile. -- **[Grout](grout.md)**: Linux handheld companion for muOS / NextUI devices. -- **[Playnite Plugin](playnite-plugin.md)**: Windows desktop, imports your RomM library into [Playnite](https://playnite.link). -- **[muOS App](muos-app.md)**: official app for muOS / EmulationStation handhelds to fetch games wirelessly. +- **[Argosy Launcher](argosy.md)**: Android launcher that browses and launches your RomM library on mobile +- **[Grout](grout.md)**: Linux handheld companion for muOS / NextUI devices +- **[Playnite Plugin](playnite-plugin.md)**: Windows desktop, imports your RomM library into [Playnite](https://playnite.link) +- **[muOS App](muos-app.md)**: official app for muOS / EmulationStation handhelds to fetch games wirelessly ## Feeds (for third-party apps) RomM exposes several URL feed endpoints for external homebrew / custom firmware apps that already know how to consume them. -- **[Tinfoil](tinfoil.md)**: Nintendo Switch homebrew for installing `.nsp` / `.xci` from a URL. -- **[pkgj](pkgj.md)**: PS Vita and PSP homebrew installer. -- **[fpkgi](fpkgi.md)**: PS4 / PS5 installer. -- **[Kekatsu](kekatsu.md)**: Nintendo DS multiboot loader. -- **[WebRcade](webrcade.md)**: browser-based retro console frontend. +- **[Tinfoil](tinfoil.md)**: Nintendo Switch homebrew for installing `.nsp` / `.xci` from a URL +- **[pkgj](pkgj.md)**: PS Vita and PSP homebrew installer +- **[fpkgi](fpkgi.md)**: PS4 / PS5 installer +- **[Kekatsu](kekatsu.md)**: Nintendo DS multiboot loader +- **[WebRcade](webrcade.md)**: browser-based retro console frontend See the [full feeds reference](../reference/feeds.md) for URL formats, auth requirements, and filtering. @@ -36,33 +36,33 @@ See **[Community Apps](community-apps.md)** for the full list with status flags Highlights: -- **romm-ios-app** (iOS native). -- **romm-mobile** (Android + iOS). -- **RommBrowser** (Electron desktop). -- **RomMate** (desktop). -- **romm-client** (desktop). -- **DeckRommSync** (Steam Deck). -- **SwitchRomM** (Nintendo Switch homebrew NRO). -- **RetroArch Sync**. -- **romm-comm** (Discord bot). -- **GGRequestz** (game request tracker). -- **Syncthing Sync**. +- **romm-ios-app** (iOS native) +- **romm-mobile** (Android + iOS) +- **RommBrowser** (Electron desktop) +- **RomMate** (desktop) +- **romm-client** (desktop) +- **DeckRommSync** (Steam Deck) +- **SwitchRomM** (Nintendo Switch homebrew NRO) +- **RetroArch Sync** +- **romm-comm** (Discord bot) +- **GGRequestz** (game request tracker) +- **Syncthing Sync** ## Build your own For developers building something new on top of RomM: -- **[Client API Tokens](client-api-tokens.md)**: how to authenticate your app, how the device-pairing flow works. -- **[Device Sync Protocol](device-sync-protocol.md)**: wire-level reference for save/state/play-session sync. -- **[API Reference](../developers/api-reference.md)**: every REST endpoint. -- **[WebSockets](../developers/websockets.md)**: live-update channels and Netplay coordination. -- **[Consuming OpenAPI](../developers/openapi.md)**: codegen patterns. +- **[Client API Tokens](client-api-tokens.md)**: how to authenticate your app, how the device-pairing flow works +- **[Device Sync Protocol](device-sync-protocol.md)**: wire-level reference for save/state/play-session sync +- **[API Reference](../developers/api-reference.md)**: every REST endpoint +- **[WebSockets](../developers/websockets.md)**: live-update channels and Netplay coordination +- **[Consuming OpenAPI](../developers/openapi.md)**: codegen patterns ## External tooling Not a RomM companion but useful alongside: -- **[Igir Collection Manager](igir.md)**: ROM sorting/verifying tool that cleans up library layout before importing into RomM. +- **[Igir Collection Manager](igir.md)**: ROM sorting/verifying tool that cleans up library layout before importing into RomM ## Contributing a companion app diff --git a/docs/ecosystem/kekatsu.md b/docs/ecosystem/kekatsu.md index de7b09e2..f8332bf6 100644 --- a/docs/ecosystem/kekatsu.md +++ b/docs/ecosystem/kekatsu.md @@ -11,9 +11,9 @@ New in RomM 5.0. ## Prerequisites -- A Nintendo DS with Kekatsu installed (requires a flashcart or homebrew launcher). +- A Nintendo DS with Kekatsu installed (requires a flashcart or homebrew launcher) - **RomM reachable from the DS over Wi-Fi.** The DS's Wi-Fi is WEP / old WPA only, so this typically means a dedicated legacy-SSID on your router or a travel router bridging the DS to your modern network. -- DS games in `.nds` format. +- DS games in `.nds` format ## Feed URL @@ -44,18 +44,18 @@ Kekatsu can send basic auth. Either configure it on the DS side or enable `DISAB The DS's original Wi-Fi hardware supports WEP and an older WPA variant only. Modern home routers usually don't. Workarounds: - **Dedicated DS-friendly SSID.** Many routers allow per-SSID security, so add a WEP one just for the DS. -- **Travel router in bridge mode.** A cheap travel router configured for WEP uplinks to your main (secure) network. +- **Travel router in bridge mode.** A cheap travel router configured for WEP uplinks to your main (secure) network - **Use a DSi, 3DS, or homebrew replacement driver.** These support modern security. If none of this is appealing, Kekatsu-over-LAN isn't going to work. Fall back to sideloading via flashcart or similar. ## Troubleshooting -- **Feed is empty.** No `.nds` files on the `nds` platform. +- **Feed is empty.** No `.nds` files on the `nds` platform - **DS can't see the network.** See the legacy-Wi-Fi section above. - **Downloads fail.** Either network timeout (LAN latency over WEP is rough) or disk space. Retry one game at a time. ## See also -- [Feeds reference](../reference/feeds.md): all feed endpoints. +- [Feeds reference](../reference/feeds.md): all feed endpoints - Kekatsu upstream: find via the RomM Discord `#kekatsu` or community links (project moves). diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md index a01ed593..09b0d4d8 100644 --- a/docs/ecosystem/muos-app.md +++ b/docs/ecosystem/muos-app.md @@ -21,8 +21,8 @@ description: Official RomM app for muOS and EmulationStation handhelds, fetch ga This page covers the **muOS App**: a lightweight client focused on game fetching. For the fuller push/pull sync experience (saves back to RomM, play-session reporting), use [Grout](grout.md) instead. They're two different clients for the same family of devices. -- **muOS App**: lightweight, pulls ROMs, no save sync. -- **Grout**: full sync, ROMs + saves + states + play sessions. +- **muOS App**: lightweight, pulls ROMs, no save sync +- **Grout**: full sync, ROMs + saves + states + play sessions Pick based on what you need. @@ -65,7 +65,7 @@ The handheld has to reach your RomM instance over Wi-Fi. Simplest setup: -- **Same LAN.** Handheld and RomM server on the same SSID. `HOST` = server IP + port. +- **Same LAN.** Handheld and RomM server on the same SSID. `HOST` = server IP + port - **Plain HTTP works** on a trusted LAN, and no reverse proxy is needed. More-involved setups: @@ -82,8 +82,8 @@ Downloaded games appear in the device's usual ROM folder for the platform, so mu ## What it doesn't do (yet) - **Save sync.** This app is pull-only. For bidirectional sync, use [Grout](grout.md). -- **Play session tracking.** Not ingested into RomM. -- **Firmware download.** Not in scope. +- **Play session tracking.** Not ingested into RomM +- **Firmware download.** Not in scope If you need these, Grout is the app. @@ -95,7 +95,7 @@ If you need these, Grout is the app. ## See also -- [Grout](grout.md): the fuller sync client for the same device family. -- [Client API Tokens](client-api-tokens.md): safer auth than plaintext credentials in `.env`. -- [Mobile & TV](../using/mobile-and-tv.md): handheld usage patterns. -- [rommapp/muos-app](https://github.com/rommapp/muos-app): source, issues, releases. +- [Grout](grout.md): the fuller sync client for the same device family +- [Client API Tokens](client-api-tokens.md): safer auth than plaintext credentials in `.env` +- [Mobile & TV](../using/mobile-and-tv.md): handheld usage patterns +- [rommapp/muos-app](https://github.com/rommapp/muos-app): source, issues, releases diff --git a/docs/ecosystem/pkgj.md b/docs/ecosystem/pkgj.md index c0cb766f..0c524a6c 100644 --- a/docs/ecosystem/pkgj.md +++ b/docs/ecosystem/pkgj.md @@ -9,10 +9,10 @@ description: Install PS Vita and PSP games from your RomM library via pkgj homeb ## Prerequisites -- **PS Vita** with [pkgj](https://github.com/blastrock/pkgj) installed. +- **PS Vita** with [pkgj](https://github.com/blastrock/pkgj) installed - A way to edit files on the Vita: [VitaShell](https://github.com/TheOfficialFloW/VitaShell) works well. - **RomM reachable from the Vita**: same LAN ideal, and HTTP or HTTPS both work. -- Your games stored as `.pkg` files (pkgj requires this format, and it won't work with `.iso` or other formats). +- Your games stored as `.pkg` files (pkgj requires this format, and it won't work with `.iso` or other formats) ## Feed URLs @@ -66,11 +66,11 @@ Unlike [Tinfoil](tinfoil.md), pkgj handles auth natively: you don't have to turn - **"can't get list: list is empty".** You don't have any `.pkg` content for the feeds you configured. Check your library actually contains `.pkg` files on the corresponding platforms. - **Refresh returns an error.** URL in `config.txt` is wrong. Verify the feed URL in a browser first (returns JSON). - **Download fails partway.** LAN connectivity or disk space on Vita. pkgj reports both. -- **Game installs but won't boot.** The `.pkg` is for a different firmware / region. Not a RomM issue. +- **Game installs but won't boot.** The `.pkg` is for a different firmware / region. Not a RomM issue ## See also -- [Feeds reference](../reference/feeds.md): full feeds catalogue. -- [Tinfoil](tinfoil.md): Switch equivalent. -- [fpkgi](fpkgi.md): PS4 / PS5 equivalent. -- [pkgj](https://github.com/blastrock/pkgj): upstream homebrew. +- [Feeds reference](../reference/feeds.md): full feeds catalogue +- [Tinfoil](tinfoil.md): Switch equivalent +- [fpkgi](fpkgi.md): PS4 / PS5 equivalent +- [pkgj](https://github.com/blastrock/pkgj): upstream homebrew diff --git a/docs/ecosystem/playnite-plugin.md b/docs/ecosystem/playnite-plugin.md index ceb55153..d0db7b57 100644 --- a/docs/ecosystem/playnite-plugin.md +++ b/docs/ecosystem/playnite-plugin.md @@ -20,10 +20,10 @@ description: Import your RomM library into Playnite on Windows. ## What it does -- Queries the RomM API to pull your library metadata. -- Creates Playnite library entries for every RomM game. -- Downloads a ROM on demand when you click **Install** in Playnite. -- Launches via your configured emulator (Playnite's existing emulator config). +- Queries the RomM API to pull your library metadata +- Creates Playnite library entries for every RomM game +- Downloads a ROM on demand when you click **Install** in Playnite +- Launches via your configured emulator (Playnite's existing emulator config) ## Installing @@ -31,8 +31,8 @@ Four paths, pick whichever's easiest: - **A.** Paste this URL into a browser to launch Playnite and install automatically: `playnite://playnite/installaddon/RomM_9700aa21-447d-41b4-a989-acd38f407d9f` -- **B.** [Playnite add-ons website](https://playnite.link/addons.html#RomM_9700aa21-447d-41b4-a989-acd38f407d9f) → Install. -- **C.** From inside Playnite: **Menu → Add-ons → Browse → Libraries**, search `RomM`, Install. +- **B.** [Playnite add-ons website](https://playnite.link/addons.html#RomM_9700aa21-447d-41b4-a989-acd38f407d9f) → Install +- **C.** From inside Playnite: **Menu → Add-ons → Browse → Libraries**, search `RomM`, Install - **D.** Download the `.pext` file from [GitHub Releases](https://github.com/rommapp/playnite-plugin/releases/latest) and drop it onto Playnite. ## Setup @@ -111,6 +111,6 @@ For automatic refresh: use Playnite's scheduled-library-refresh add-on, or manua ## See also -- [Client API Tokens](client-api-tokens.md): recommended auth method. -- [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin): source, issues, releases. -- [Playnite docs](https://playnite.link/docs/): Playnite basics if you're new to it. +- [Client API Tokens](client-api-tokens.md): recommended auth method +- [rommapp/playnite-plugin](https://github.com/rommapp/playnite-plugin): source, issues, releases +- [Playnite docs](https://playnite.link/docs/): Playnite basics if you're new to it diff --git a/docs/ecosystem/tinfoil.md b/docs/ecosystem/tinfoil.md index 2dc23ce8..a8679f47 100644 --- a/docs/ecosystem/tinfoil.md +++ b/docs/ecosystem/tinfoil.md @@ -15,7 +15,7 @@ description: Install Nintendo Switch games from your RomM library over Wi-Fi via ## Prerequisites -- **RomM 3.5.0 or newer.** Tinfoil feeds landed in that release. Much better in 5.0. +- **RomM 3.5.0 or newer.** Tinfoil feeds landed in that release. Much better in 5.0 - **`DISABLE_DOWNLOAD_ENDPOINT_AUTH=true`** on your RomM instance. Tinfoil can't send a bearer token, so the downloads endpoint has to be openable. **Only enable this when RomM isn't directly exposed to the public internet.** See [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass). - **Tinfoil installed on the Switch.** Setup varies, so follow Tinfoil's own docs. - **A Switch that can reach RomM over Wi-Fi.** Same LAN is easiest. Remote reachability requires reverse proxy + cert that the Switch accepts. @@ -33,14 +33,14 @@ No authentication: the endpoint works as long as `DISABLE_DOWNLOAD_ENDPOINT_AUTH 1. Launch Tinfoil on the Switch → **File Browser**. 2. Scroll to the file-server list → press `-` (minus) to add a new one. 3. Enter: - - **Protocol:** `http` or `https` (depending on your RomM reverse-proxy setup). - - **Host:** RomM's hostname or IP. - - **Port:** RomM's port (usually 80 or 443). + - **Protocol:** `http` or `https` (depending on your RomM reverse-proxy setup) + - **Host:** RomM's hostname or IP + - **Port:** RomM's port (usually 80 or 443) - **Path:** `/api/feeds/tinfoil` - - **Username:** your RomM username (optional, Tinfoil can send basic auth, and RomM tries it). - - **Password:** your RomM password. - - **Title:** anything (e.g. `RomM Switch`). - - **Enabled:** yes. + - **Username:** your RomM username (optional, Tinfoil can send basic auth, and RomM tries it) + - **Password:** your RomM password + - **Title:** anything (e.g. `RomM Switch`) + - **Enabled:** yes 4. Press `X` to save. 5. Close and reopen Tinfoil. The library is parsed. @@ -50,7 +50,7 @@ On reopen, you should see a custom message of the day: `RomM Switch Library`. If ## Using it -- **New Games** tab in Tinfoil: browseable list of your Switch ROMs. +- **New Games** tab in Tinfoil: browseable list of your Switch ROMs - **File Browser**: pick a file to install directly. Tinfoil handles the install flow like any homebrew: downloads the `.nsp` / `.xci`, installs to eMMC or SD, cleans up. @@ -101,5 +101,5 @@ This gets you authenticated Tinfoil feeds without making RomM itself world-reada ## See also -- [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass): the `DISABLE_DOWNLOAD_ENDPOINT_AUTH` caveat. -- [Feeds reference](../reference/feeds.md): full feeds catalogue. +- [Authentication → Download-endpoint auth bypass](../administration/authentication.md#download-endpoint-auth-bypass): the `DISABLE_DOWNLOAD_ENDPOINT_AUTH` caveat +- [Feeds reference](../reference/feeds.md): full feeds catalogue diff --git a/docs/ecosystem/webrcade.md b/docs/ecosystem/webrcade.md index 371fd025..5729cdd2 100644 --- a/docs/ecosystem/webrcade.md +++ b/docs/ecosystem/webrcade.md @@ -39,7 +39,7 @@ The same security caveats apply. See [Tinfoil prerequisites](tinfoil.md#prerequi Why would you use WebRcade over RomM's built-in player? - **Preset/curated lists.** WebRcade maintains a catalogue of vetted content that's searchable inside the app. -- **Different UI.** More console-like, less library-like. +- **Different UI.** More console-like, less library-like - **Per-game launch from WebRcade feeds.** You can mix-and-match RomM content with WebRcade's own catalogue in one frontend. When to stay with RomM's player: @@ -58,6 +58,6 @@ Totally reasonable to run both: WebRcade as a launcher UI pointed at RomM for th ## See also -- [Feeds reference](../reference/feeds.md): all feed endpoints. -- [In-Browser Play](../using/in-browser-play.md): RomM's built-in player. -- [WebRcade](https://www.webrcade.com/): upstream frontend. +- [Feeds reference](../reference/feeds.md): all feed endpoints +- [In-Browser Play](../using/in-browser-play.md): RomM's built-in player +- [WebRcade](https://www.webrcade.com/): upstream frontend diff --git a/docs/getting-started/first-scan.md b/docs/getting-started/first-scan.md index af1b7c20..6756701c 100644 --- a/docs/getting-started/first-scan.md +++ b/docs/getting-started/first-scan.md @@ -41,7 +41,7 @@ For each ROM the scanner: An **unmatched** ROM means no provider recognised it, with common causes: - Filename is too generic (`game.gba`). -- Bad rip, intro/patch applied, or a regional variant no provider has indexed. +- Bad rip, intro/patch applied, or a regional variant no provider has indexed - Platform folder misnamed: the scanner queries providers scoped to the detected platform, so wrong platform equals no results. - Metadata provider credentials wrong or rate-limited: check the scan log for errors or the metadata provider status page in the admin dashboard. @@ -51,16 +51,16 @@ Most of these are fixable: see [Scanning Troubleshooting](../troubleshooting/sca Click the **RomM logo** (top-left) to go home. You should see: -- Platform cards for each folder it scanned. -- A **Recently Added** carousel on the dashboard. -- A **Continue Playing** section (empty until you play something). +- Platform cards for each folder it scanned +- A **Recently Added** carousel on the dashboard +- A **Continue Playing** section (empty until you play something) From here, typical next steps: - **Browse**: click a platform card, flip through the grid. - **Fix unmatched ROMs**: rename or re-tag, then re-run an **Unmatched** scan to pick them up. See [Scanning & Watcher](../administration/scanning-and-watcher.md#scan-modes). - **Tweak priorities**: if ScreenScraper's covers are nicer than IGDB's for your library, reorder `scan.priority.artwork` in [`config.yml`](../reference/configuration-file.md). -- **Add more users**: [Invitations & Registration](../administration/invitations-and-registration.md). +- **Add more users**: [Invitations & Registration](../administration/invitations-and-registration.md) ## Skip to a targeted scan @@ -77,7 +77,7 @@ All six scan modes are documented in [Scanning & Watcher](../administration/scan Don't want to keep clicking Scan? - **Scheduled scans** run nightly by default. Tune with `SCAN_INTERVAL_CRON`. -- **The filesystem watcher** can auto-scan when files appear or disappear. Enable with `WATCHER_ENABLED=true`. Details and tradeoffs in [Scanning & Watcher](../administration/scanning-and-watcher.md). +- **The filesystem watcher** can auto-scan when files appear or disappear. Enable with `WATCHER_ENABLED=true`. Details and tradeoffs in [Scanning & Watcher](../administration/scanning-and-watcher.md) ## It's working: what next? diff --git a/docs/getting-started/folder-structure.md b/docs/getting-started/folder-structure.md index 99db9244..b836c2a5 100644 --- a/docs/getting-started/folder-structure.md +++ b/docs/getting-started/folder-structure.md @@ -13,14 +13,14 @@ RomM expects your library to be organised in one of two layouts. It tries **Stru Both layouts separate ROMs from BIOS files. They differ on whether the split lives at the top of the tree or inside each platform. -- **Structure A (recommended)**: one top-level `roms/`, one top-level `bios/`, platforms nested inside each. +- **Structure A (recommended)**: one top-level `roms/`, one top-level `bios/`, platforms nested inside each ```text /roms/{platform}/ /bios/{platform}/ ``` -- **Structure B (fallback)**: one folder per platform at the top, `roms/` and `bios/` inside each. +- **Structure B (fallback)**: one folder per platform at the top, `roms/` and `bios/` inside each ```text /{platform}/roms/ @@ -163,8 +163,8 @@ The on-disk layout is only half the story. Per-library exclusions, custom platfo Filenames are parsed for region, language, revision, and arbitrary tags. Both `[]` and `()` delimiters work. - **Region / language**: both ISO-like codes and full names. Add a custom region or language by prefixing with `reg` / `reg-` (e.g. `reg MyOwnLang` or `reg-MyOwnLang`). -- **Revision**: prefix with `rev` / `rev-`. Example: `rev v1`, `rev-1`. -- **Arbitrary tags**: anything else in brackets is imported verbatim. Example: `tetris [1.0001](HACK)[!].gba`. +- **Revision**: prefix with `rev` / `rev-`. Example: `rev v1`, `rev-1` +- **Arbitrary tags**: anything else in brackets is imported verbatim. Example: `tetris [1.0001](HACK)[!].gba` RomM also honours inline tags like `(igdb-1234)` in filenames to force a match to a specific provider entry, covered in [Metadata Providers → Filename tags](../administration/metadata-providers.md#metadata-tags-in-filenames). diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index f9226bdf..17549ad4 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -11,8 +11,8 @@ This guide gets a RomM instance up and running with the default stack (MariaDB + You'll need: -- [Docker](https://docs.docker.com/get-docker/) and Docker Compose installed on the host. -- Your ROM files organised in the expected [folder structure](folder-structure.md). +- [Docker](https://docs.docker.com/get-docker/) and Docker Compose installed on the host +- Your ROM files organised in the expected [folder structure](folder-structure.md) - API credentials for at least one [metadata provider](../administration/metadata-providers.md). Hasheous + IGDB + SteamGridDB + Retroachievements is the recommended pairing. RomM will run without any provider configured but matching quality will suffer. diff --git a/docs/index.md b/docs/index.md index 4af763c1..1ba74bfc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -87,4 +87,4 @@ RomM is built for its users, not for shareholders: self-hosted, open-source, no
-Join us on Discord to ask questions, share your setup, request features, or help other users; code, issues, and releases live on [GitHub](https://github.com/rommapp/romm). +Join us on Discord to ask questions, share your setup, request features, or help other users. Code, issues, and releases live on [GitHub](https://github.com/rommapp/romm). diff --git a/docs/install/backup-and-restore.md b/docs/install/backup-and-restore.md index 87b4dfbe..9c0199e2 100644 --- a/docs/install/backup-and-restore.md +++ b/docs/install/backup-and-restore.md @@ -13,7 +13,7 @@ This page covers both routine backups and migrating RomM to a new host. | -------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | **Database** (`mysql_data`, `pg_data`, etc.) | User accounts, ROM metadata, collections, ratings, play sessions, paired devices, saves/states metadata | **Critical**: back this up nightly. | | **`/romm/assets`** | User uploads: save files, save states, user-uploaded screenshots, manuals, covers | **Critical**: back this up nightly. | -| **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes but small and painful to recreate. | +| **`/romm/config`** | `config.yml` and any custom overrides | **Critical**: rarely changes but small and painful to recreate. | | `/romm/resources` | Metadata images (covers, screenshots) fetched from IGDB/ScreenScraper/etc. | Low priority, and can be re-downloaded on a rescan. Including it speeds up recovery. | | `/redis-data` | Task queue state | Low priority, in-flight tasks only, and lost tasks can be re-run. | | **`/romm/library`** | Your ROM files | Back this up **separately**. It's your source data and you should already have a backup strategy for it independent of RomM. | diff --git a/docs/install/databases.md b/docs/install/databases.md index 1a8d88dd..876c7e0a 100644 --- a/docs/install/databases.md +++ b/docs/install/databases.md @@ -24,7 +24,7 @@ This is what the [reference Compose](docker-compose.md) sets up. No extra config services: romm: environment: - - ROMM_DB_DRIVER=mariadb # optional; this is the default + - ROMM_DB_DRIVER=mariadb # this is the default - DB_HOST=romm-db - DB_PORT=3306 - DB_NAME=romm @@ -129,7 +129,7 @@ Exact keys depend on the driver. See SQLAlchemy / the driver's docs. ## Which should I pick? - **Sticking with defaults?** MariaDB. That's what the reference compose uses and what the team tests against. -- **Already run Postgres?** Postgres. No reason to add a second DB engine. +- **Already run Postgres?** Postgres. No reason to add a second DB engine - **Single-user laptop demo?** SQLite is fine but upgrade before adding anyone else. - **External managed DB?** Any of MariaDB / MySQL / Postgres. Point `DB_HOST` at it and configure TLS via `DB_QUERY_JSON`. diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index 5057b438..db8521af 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -22,12 +22,12 @@ The RomM stack has three parts: ### `romm` -| Field | Value | Notes | -| ------------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`; pin to a specific tag (`5.0.0`) for production, and see [Image Variants](image-variants.md) for `slim` vs `full`. | -| `ports` | `80:8080` | Container listens on `8080`; expose through a reverse proxy in production (see [Reverse Proxy](reverse-proxy.md)). | -| `volumes` | see below | RomM writes to four distinct paths inside the container. | -| `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | +| Field | Value | Notes | +| ------------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `image` | `rommapp/romm:latest` | Or `ghcr.io/rommapp/romm:latest`. Ideally, pin it to a specific tag (`5.0.0`) and see [Image Variants](image-variants.md) for `slim` vs `full`. | +| `ports` | `80:8080` | Container listens on `8080` | +| `volumes` | see below | RomM writes to four distinct paths inside the container. | +| `depends_on` | `romm-db` (healthy) | RomM exits on startup if the DB isn't reachable. | #### Volumes @@ -71,7 +71,7 @@ The quick-start compose file is functional but not production-ready. Before expo - **Set a non-default `MARIADB_ROOT_PASSWORD`**, and don't reuse it for `MARIADB_PASSWORD`. - **Mount the library read-only** unless you need RomM to write into it: `- /path/to/library:/romm/library:ro`. - **Use Docker secrets for credentials** if your orchestrator supports them: RomM recognises `ROMM_AUTH_SECRET_KEY_FILE` for loading the secret from a file mount. -- **Enable backups**: at minimum, daily `mysqldump` + rsync of `/romm/assets` and `/romm/config` (see [Backup & Restore](backup-and-restore.md)). +- **Enable backups**: at minimum, daily `mysqldump` + rsync of `/romm/assets` and `/romm/config` (see [Backup & Restore](backup-and-restore.md)) ## Updating diff --git a/docs/install/index.md b/docs/install/index.md index 12e02280..704e33e0 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -23,14 +23,14 @@ If none of those match, start with [Docker Compose](docker-compose.md) and adapt Regardless of host platform, you'll make the same handful of decisions: -- **[Image variant](image-variants.md)**: `full` (default, includes in-browser emulators) or `slim` (headless, smaller memory footprint). -- **[Database](databases.md)**: MariaDB (default), MySQL or PostgreSQL. -- **[In-memory store](redis-or-valkey.md)**: required for sessions + task queue, embedded or external. +- **[Image variant](image-variants.md)**: `full` (default, includes in-browser emulators) or `slim` (headless, smaller memory footprint) +- **[Database](databases.md)**: MariaDB (default), MySQL or PostgreSQL +- **[In-memory store](redis-or-valkey.md)**: required for sessions + task queue, embedded or external - **[Reverse proxy](reverse-proxy.md)**: Caddy, nginx, Traefik, or NPM. HTTPS is required for OIDC and PWA install. - **[Backup & restore](backup-and-restore.md)**: Test it before you need it! ## After you're up and running -- **Configure metadata providers**: [Metadata Providers](../administration/metadata-providers.md). -- **Populate the library**: [Your First Scan](../getting-started/first-scan.md). -- **Add users**: [Invitations & Registration](../administration/invitations-and-registration.md). +- **Configure metadata providers**: [Metadata Providers](../administration/metadata-providers.md) +- **Populate the library**: [Your First Scan](../getting-started/first-scan.md) +- **Add users**: [Invitations & Registration](../administration/invitations-and-registration.md) diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index e01cc73a..d81fe584 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -13,10 +13,10 @@ There's no official Helm chart for RomM in 5.0. This page walks through a produc ## What you need -- A cluster running Kubernetes 1.27+. -- Persistent storage for the DB, cache, and RomM's assets/resources/config (block or RWX, see below). -- An Ingress controller (nginx-ingress, Traefik, etc.) for external access. -- cert-manager or equivalent for HTTPS. +- A cluster running Kubernetes 1.27+ +- Persistent storage for the DB, cache, and RomM's assets/resources/config (block or RWX, see below) +- An Ingress controller (nginx-ingress, Traefik, etc.) for external access +- cert-manager or equivalent for HTTPS ## Required quirk: `enableServiceLinks: false` diff --git a/docs/install/redis-or-valkey.md b/docs/install/redis-or-valkey.md index 3629c01e..0d36c584 100644 --- a/docs/install/redis-or-valkey.md +++ b/docs/install/redis-or-valkey.md @@ -7,10 +7,10 @@ description: Why RomM needs Redis, how it's run, and when to split it out. RomM needs a Redis-protocol server. It's used for: -- **Session storage**: login sessions, CSRF tokens. +- **Session storage**: login sessions, CSRF tokens - **Background task queue**: scans, metadata syncs, sync operations run through RQ (Redis Queue). -- **Cache**: metadata lookups, heartbeat data, paired-device tokens, rate-limit counters. -- **Socket.IO**: multi-instance pubsub for live updates (only relevant if you're running more than one RomM container). +- **Cache**: metadata lookups, heartbeat data, paired-device tokens, rate-limit counters +- **Socket.IO**: multi-instance pubsub for live updates (only relevant if you're running more than one RomM container) Both **Redis** and **Valkey** (the open-source Redis fork maintained by the Linux Foundation after Redis Inc.'s license change) are supported. They're interchangeable: Valkey is a drop-in wire-compatible replacement. Pick either. @@ -24,9 +24,9 @@ The default `full-image` container runs a Redis server inside itself. Zero confi Better for: -- Any production instance you care about. -- Multiple RomM replicas sharing state. -- Homelabs that already run a Redis box and want consistency. +- Any production instance you care about +- Multiple RomM replicas sharing state +- Homelabs that already run a Redis box and want consistency ```yaml services: @@ -111,8 +111,8 @@ docker exec romm redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWD" If you see `PONG` from the RomM container, you're good. If not, check: -- That the DNS name in `REDIS_HOST` resolves from the RomM container (use `docker exec romm getent hosts romm-redis`). +- That the DNS name in `REDIS_HOST` resolves from the RomM container (use `docker exec romm getent hosts romm-redis`) - That the password is correct. `redis-cli -a` will say `NOAUTH` if wrong. -- That `REDIS_SSL=true` matches whether the server actually requires TLS. +- That `REDIS_SSL=true` matches whether the server actually requires TLS Debugging further: see the Redis line in `docker logs romm` at startup, where RomM logs the connection target. diff --git a/docs/install/synology.md b/docs/install/synology.md index 9a931029..17f40adf 100644 --- a/docs/install/synology.md +++ b/docs/install/synology.md @@ -16,9 +16,9 @@ Follow those if they match your setup. The walkthrough below is the fallback for ## Prerequisites -- A Synology NAS with Container Manager (DSM 7.2+) or the legacy Docker package. -- SSH or a task that lets you run shell commands on the NAS. -- Basic comfort with Docker Compose. +- A Synology NAS with Container Manager (DSM 7.2+) or the legacy Docker package +- SSH or a task that lets you run shell commands on the NAS +- Basic comfort with Docker Compose ## 1. Create folders diff --git a/docs/install/truenas.md b/docs/install/truenas.md index d9567f80..090d35d2 100644 --- a/docs/install/truenas.md +++ b/docs/install/truenas.md @@ -9,9 +9,9 @@ This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported because its Fr ## Prerequisites -- A running TrueNAS SCALE installation. -- Your ROM library arranged in the expected [folder structure](../getting-started/folder-structure.md). -- A TrueNAS user/group with a UID/GID that can own the dataset paths you'll mount into RomM. +- A running TrueNAS SCALE installation +- Your ROM library arranged in the expected [folder structure](../getting-started/folder-structure.md) +- A TrueNAS user/group with a UID/GID that can own the dataset paths you'll mount into RomM ## Option A: App Catalog (recommended) @@ -75,8 +75,8 @@ In at least one reported setup, creating a user/group in TrueNAS with UID/GID `1 ### Other issues -- [Scanning Troubleshooting](../troubleshooting/scanning.md) for matching / ingest problems. -- [Authentication Troubleshooting](../troubleshooting/authentication.md) for login issues. +- [Scanning Troubleshooting](../troubleshooting/scanning.md) for matching / ingest problems +- [Authentication Troubleshooting](../troubleshooting/authentication.md) for login issues - The RomM [Discord](https://discord.gg/P5HtHnhUDH) has a `#truenas` channel with active community troubleshooting. ## Contributing diff --git a/docs/install/unraid.md b/docs/install/unraid.md index 8be11b85..6788ddad 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -7,8 +7,8 @@ description: Install RomM on Unraid via the Community Apps template or the Docke Two supported install paths on Unraid. Pick one: -- **[Community Apps template](#community-apps-template)**: simplest. Install RomM and MariaDB as separate CA templates. Good for users who already manage containers one-at-a-time. -- **[Docker Compose Manager](#docker-compose-manager)**: closer to the upstream reference setup. Drops the standard `docker-compose.yml` in and uses the Compose plugin to manage it. Recommended if you're comfortable editing Compose files and want parity with other deployments. +- **[Community Apps template](#community-apps-template)**: simplest. Install RomM and MariaDB as separate CA templates. Good for users who already manage containers one-at-a-time +- **[Docker Compose Manager](#docker-compose-manager)**: closer to the upstream reference setup. Drops the standard `docker-compose.yml` in and uses the Compose plugin to manage it. Recommended if you're comfortable editing Compose files and want parity with other deployments Both end up with the same running stack. @@ -22,7 +22,7 @@ Both end up with the same running stack. ### Prerequisites -- [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) installed. +- [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) installed - A custom Docker bridge network so RomM and MariaDB can talk to each other by container name. Skip this and you'll hit DNS issues that look like everything else. ```sh @@ -68,8 +68,8 @@ Apply, head back to the **Docker** tab, and you should see both containers runni ### Prerequisites -- [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/). -- [Docker Compose Manager plugin](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/) from CA. +- [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) +- [Docker Compose Manager plugin](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/) from CA ![Docker Compose Addon](../resources/unraid/docker-compose.png) diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index 65ca8685..8cc0b67e 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -84,7 +84,7 @@ Same mechanic: just use a filename matching an existing platform's slug and your - **Metadata provider coverage.** Providers are IGDB-slug-driven. A genuinely unknown platform won't have IGDB / ScreenScraper / MobyGames data. - **EmulatorJS support.** The platform has to match a known EmulatorJS core. See [Supported Platforms → EmulatorJS column](supported-platforms.md). -- **RetroAchievements.** Hash-based but restricted to RA-supported platforms. +- **RetroAchievements.** Hash-based but restricted to RA-supported platforms For the niche platform case, you'll likely rely on filename-only matching (no provider) and browsing the library like a straight file system. @@ -92,10 +92,10 @@ For the niche platform case, you'll likely rely on filename-only matching (no pr If you've added a custom platform that should be supported natively by RomM (e.g. a widely-used platform that's missing) open an issue on [rommapp/romm](https://github.com/rommapp/romm/issues) with: -- Platform name. -- IGDB platform URL (if it exists on IGDB). -- Typical file extensions. -- Suggested canonical slug. +- Platform name +- IGDB platform URL (if it exists on IGDB) +- Typical file extensions +- Suggested canonical slug The team adds platforms fairly aggressively if they see community demand. diff --git a/docs/platforms/emulatorjs-config.md b/docs/platforms/emulatorjs-config.md index 1c4e54b1..b5931f7d 100644 --- a/docs/platforms/emulatorjs-config.md +++ b/docs/platforms/emulatorjs-config.md @@ -31,7 +31,7 @@ Logs every available option for each core to the browser console when a game lau ```yaml emulatorjs: - cache_limit: 52428800 # 50 MB per ROM; null = unlimited + cache_limit: 52428800 # 50 MB per ROM ``` Each ROM's cache (core files, BIOS, settings) is capped. `null` removes the cap: useful for large PSP / Saturn libraries on hosts with plenty of disk. @@ -45,7 +45,7 @@ emulatorjs: ``` - **`disable_batch_bootup`**: skips the `autorun.bat` step when `dosbox-pure` loads. Try this if DOS games hang on boot. See [MS-DOS](ms-dos.md). -- **`disable_auto_unload`**: keeps the emulator running when the user navigates away. Default off (browsers unload the emulator on page change). +- **`disable_auto_unload`**: keeps the emulator running when the user navigates away. Default off (browsers unload the emulator on page change) ## Netplay @@ -122,11 +122,11 @@ emulatorjs: The structure: -- Top level = core name. -- Second level = player number (`0`, `1`, `2`, `3`). +- Top level = core name +- Second level = player number (`0`, `1`, `2`, `3`) - Third level = button slot (core-specific, and 0 is usually the first face button). -- `value` = keyboard key. -- `value2` = gamepad button. +- `value` = keyboard key +- `value2` = gamepad button Slot indexes and what they map to vary per core. See [EmulatorJS control-mapping docs](https://emulatorjs.org/docs4devs/control-mapping/). @@ -165,8 +165,8 @@ Per-user overrides take precedence. A config.yml setting is the fallback. For frontend parity: -- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml): Batocera / RetroBat control + setting layouts. -- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml): ES-DE layout. +- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml): Batocera / RetroBat control + setting layouts +- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml): ES-DE layout ## Troubleshooting @@ -176,6 +176,6 @@ For frontend parity: ## See also -- [In-Browser Play](../using/in-browser-play.md): end-user-facing. -- [Configuration File → `emulatorjs`](../reference/configuration-file.md#emulatorjs): full schema. -- [EmulatorJS docs](https://emulatorjs.org/docs/): upstream reference. +- [In-Browser Play](../using/in-browser-play.md): end-user-facing +- [Configuration File → `emulatorjs`](../reference/configuration-file.md#emulatorjs): full schema +- [EmulatorJS docs](https://emulatorjs.org/docs/): upstream reference diff --git a/docs/platforms/firmware-by-platform.md b/docs/platforms/firmware-by-platform.md index 792edbde..5b6798d6 100644 --- a/docs/platforms/firmware-by-platform.md +++ b/docs/platforms/firmware-by-platform.md @@ -18,7 +18,7 @@ This page lists the firmware RomM's EmulatorJS cores need for each platform. For | **PlayStation (PSX)** | `ps` | `scph1001.bin` (US), `scph5501.bin` (US), `scph5502.bin` (EU), `scph7502.bin` (JP) | At least one region BIOS required. | | **Saturn** | `saturn` | `saturn_bios.bin`, optional `mpr-17933.bin` (EU) | Bundle both in a zip. | | **Sega CD / Mega CD** | `segacd` | `bios_CD_U.bin` (US), `bios_CD_E.bin` (EU), `bios_CD_J.bin` (JP) | Region depends on your games. | -| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional. Games run without it but accurate emulation needs it. | +| **Game Boy Advance** | `gba` | `gba_bios.bin` | Optional. Games run without it but accurate emulation needs it. | | **Nintendo DS** | `nds` | `bios7.bin`, `bios9.bin`, `firmware.bin` | All three are needed. | | **Atari Lynx** | `lynx` | `lynxboot.img` | | | **Neo Geo Pocket / Color** | `ngp` / `ngpc` | `neopop.rom` | Bundle as zip. | @@ -64,7 +64,7 @@ If you're not sure what name a core expects, check the [upstream EmulatorJS syst Your options (jurisdiction-dependent): -- **Dump from real hardware you own.** The cleanest answer. +- **Dump from real hardware you own.** The cleanest answer - **Community archives.** Not linked here. The RomM project doesn't host these and doesn't recommend specific sources. - **Official emulator bundles.** Some modern emulators ship with (or include a button to download) BIOS files. If you bought one, it's yours to use. @@ -81,7 +81,7 @@ Known-good hashes for common BIOS files are documented on [No-Intro](https://dat ## See also -- [Firmware Management](../administration/firmware-management.md): admin-side upload and lifecycle. -- [Supported Platforms](supported-platforms.md): catalogue with firmware-required flag. -- [In-Browser Play Troubleshooting → "Firmware required"](../troubleshooting/in-browser-play.md#firmware-required): diagnostic. -- [EmulatorJS systems reference](https://emulatorjs.org/docs/systems/): upstream firmware requirements per core. +- [Firmware Management](../administration/firmware-management.md): admin-side upload and lifecycle +- [Supported Platforms](supported-platforms.md): catalogue with firmware-required flag +- [In-Browser Play Troubleshooting → "Firmware required"](../troubleshooting/in-browser-play.md#firmware-required): diagnostic +- [EmulatorJS systems reference](https://emulatorjs.org/docs/systems/): upstream firmware requirements per core diff --git a/docs/platforms/index.md b/docs/platforms/index.md index d5249799..1df86b46 100644 --- a/docs/platforms/index.md +++ b/docs/platforms/index.md @@ -7,22 +7,22 @@ description: Everything about the platforms RomM supports: catalogue, custom map This section covers: -- **[Supported Platforms](supported-platforms.md)**: the canonical catalogue of every platform RomM ships with. Platform slugs, active metadata providers per platform, EmulatorJS support flag, and firmware requirements. -- **[Custom Platforms](custom-platforms.md)**: how to add platforms RomM doesn't know about, plus custom platform icons. -- **[MS-DOS](ms-dos.md)**: long-form guide to DOS on RomM: `dosbox-pure`, installer ROMs, autorun scripts, known issues. -- **[EmulatorJS Configuration](emulatorjs-config.md)**: operator-level tuning of the EmulatorJS player: per-core settings, control mappings, cache limits, Netplay. -- **[Ruffle Configuration](ruffle-config.md)**: Flash / Shockwave configuration. -- **[Firmware by Platform](firmware-by-platform.md)**: which firmware files each platform needs, file names, and where to get them legally. +- **[Supported Platforms](supported-platforms.md)**: the canonical catalogue of every platform RomM ships with. Platform slugs, active metadata providers per platform, EmulatorJS support flag, and firmware requirements +- **[Custom Platforms](custom-platforms.md)**: how to add platforms RomM doesn't know about, plus custom platform icons +- **[MS-DOS](ms-dos.md)**: long-form guide to DOS on RomM: `dosbox-pure`, installer ROMs, autorun scripts, known issues +- **[EmulatorJS Configuration](emulatorjs-config.md)**: operator-level tuning of the EmulatorJS player: per-core settings, control mappings, cache limits, Netplay +- **[Ruffle Configuration](ruffle-config.md)**: Flash / Shockwave configuration +- **[Firmware by Platform](firmware-by-platform.md)**: which firmware files each platform needs, file names, and where to get them legally ## Quick orientation -- **"What platforms does RomM support?"** → [Supported Platforms](supported-platforms.md). -- **"My platform isn't in the list."** → [Custom Platforms](custom-platforms.md) or map it via `system.platforms` in [`config.yml`](../reference/configuration-file.md#systemplatforms). -- **"I want to tweak how an emulator behaves."** → [EmulatorJS Configuration](emulatorjs-config.md). -- **"My game needs a BIOS."** → [Firmware by Platform](firmware-by-platform.md) + [Firmware Management](../administration/firmware-management.md). +- **"What platforms does RomM support?"** → [Supported Platforms](supported-platforms.md) +- **"My platform isn't in the list."** → [Custom Platforms](custom-platforms.md) or map it via `system.platforms` in [`config.yml`](../reference/configuration-file.md#systemplatforms) +- **"I want to tweak how an emulator behaves."** → [EmulatorJS Configuration](emulatorjs-config.md) +- **"My game needs a BIOS."** → [Firmware by Platform](firmware-by-platform.md) + [Firmware Management](../administration/firmware-management.md) ## Related sections -- **[In-Browser Play](../using/in-browser-play.md)**: the end-user side of EmulatorJS + Ruffle. -- **[Metadata Providers](../administration/metadata-providers.md)**: which providers each platform has coverage from. -- **[Folder Structure](../getting-started/folder-structure.md)**: how the platform slug maps to on-disk folder names. +- **[In-Browser Play](../using/in-browser-play.md)**: the end-user side of EmulatorJS + Ruffle +- **[Metadata Providers](../administration/metadata-providers.md)**: which providers each platform has coverage from +- **[Folder Structure](../getting-started/folder-structure.md)**: how the platform slug maps to on-disk folder names diff --git a/docs/platforms/ms-dos.md b/docs/platforms/ms-dos.md index 3b426ab6..d11edbaa 100644 --- a/docs/platforms/ms-dos.md +++ b/docs/platforms/ms-dos.md @@ -21,8 +21,8 @@ DOS games run in RomM via [`dosbox-pure`](https://github.com/schellingb/dosbox-p DOS games come in three flavours, each needing a different approach: -- **Homebrew:** indie / modern DOS games, just an `.exe` that usually Just Works once mounted. -- **Shareware demos:** what most "free DOS games" sites distribute, same as homebrew with all files in one folder. +- **Homebrew:** indie / modern DOS games, just an `.exe` that usually Just Works once mounted +- **Shareware demos:** what most "free DOS games" sites distribute, same as homebrew with all files in one folder - **Retail:** need the original CD mounted alongside the installed game files, which means more work and every game is different. ## The manual way (commandline) @@ -157,9 +157,9 @@ Retail games need the game CD image mounted alongside the install directory. Key lines: - - `Mount C ".."`: game files as `C:`. - - `imgmount d DUNGEO~8.CUE -t iso -fs iso`: CD image as `D:`. - - `cd ..`: back to `C:\` where `KEEPER.exe` lives. + - `Mount C ".."`: game files as `C:` + - `imgmount d DUNGEO~8.CUE -t iso -fs iso`: CD image as `D:` + - `cd ..`: back to `C:\` where `KEEPER.exe` lives - `KEEPER.exe`: run. (File names in DOS are 8.3: `DUNGEON8.CUE` becomes `DUNGEO~8.CUE`.) @@ -202,6 +202,6 @@ Dosemu (Linux native) and Exodos (Windows frontend) are separate DOS tools, not ## See also -- [In-Browser Play](../using/in-browser-play.md): general EmulatorJS behaviour. -- [EmulatorJS Configuration](emulatorjs-config.md): operator-level tuning, including `disable_batch_bootup` for DOS-specific issues. -- [dosbox-pure docs](https://github.com/schellingb/dosbox-pure): upstream reference for every config option. +- [In-Browser Play](../using/in-browser-play.md): general EmulatorJS behaviour +- [EmulatorJS Configuration](emulatorjs-config.md): operator-level tuning, including `disable_batch_bootup` for DOS-specific issues +- [dosbox-pure docs](https://github.com/schellingb/dosbox-pure): upstream reference for every config option diff --git a/docs/platforms/ruffle-config.md b/docs/platforms/ruffle-config.md index 6fa19e68..1b19f3c0 100644 --- a/docs/platforms/ruffle-config.md +++ b/docs/platforms/ruffle-config.md @@ -38,9 +38,9 @@ Now `web-games/` is treated as the `flash` platform. See [Configuration File → ## Supported content -- **Flash (SWF):** 2D games, most work cleanly. AS1/AS2 excellent, and AS3 still maturing in Ruffle. +- **Flash (SWF):** 2D games, most work cleanly. AS1/AS2 excellent, and AS3 still maturing in Ruffle - **Shockwave (DCR):** partial support. Complex 3D Shockwave games often fail. -- **FLV / F4V:** Flash video, playable but not game-like. +- **FLV / F4V:** Flash video, playable but not game-like [Ruffle compatibility database](https://ruffle.rs/#compatibility) has per-title status if you want to check a specific game. @@ -69,8 +69,8 @@ Then run an **Unmatched** scan on your `flash` platform. Titles, descriptions, c Flash was designed around mouse + keyboard. Ruffle passes input through natively: -- **Mouse:** full support. -- **Keyboard:** full support. +- **Mouse:** full support +- **Keyboard:** full support - **Gamepad: not supported.** Flash games using XInput or similar don't work. On a handheld / Console Mode, Flash games are generally unplayable unless you've got a touchscreen + keyboard or a mouse. @@ -82,8 +82,8 @@ Ruffle is bundled in the full RomM image. Updates to Ruffle land when RomM updat ## Not in 5.0 yet - **Per-game config overrides.** Ruffle supports some game-specific options upstream but RomM doesn't surface them yet. -- **Networking.** Flash games that hit remote servers typically fail (those servers are dead). No proxy / emulated backend for networked Flash games. -- **Control remapping.** Straight passthrough only. +- **Networking.** Flash games that hit remote servers typically fail (those servers are dead). No proxy / emulated backend for networked Flash games +- **Control remapping.** Straight passthrough only ## Troubleshooting @@ -95,5 +95,5 @@ See [In-Browser Play Troubleshooting → Ruffle](../troubleshooting/in-browser-p ## See also -- [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle): end-user side. -- [Metadata Providers → Flashpoint](../administration/metadata-providers.md#flashpoint): where Flash metadata comes from. +- [In-Browser Play → Ruffle](../using/in-browser-play.md#ruffle): end-user side +- [Metadata Providers → Flashpoint](../administration/metadata-providers.md#flashpoint): where Flash metadata comes from diff --git a/docs/platforms/supported-platforms.md b/docs/platforms/supported-platforms.md index b00d7c78..b65adfce 100644 --- a/docs/platforms/supported-platforms.md +++ b/docs/platforms/supported-platforms.md @@ -20,7 +20,7 @@ Your folder name has to match the **platform slug** in the table. If yours diffe ## What the columns mean - **Slug**: the folder name RomM expects. Matches the IGDB platform slug where possible. -- **Name**: the human-readable platform name. +- **Name**: the human-readable platform name - **Providers**: which metadata providers have at least partial coverage. See [Metadata Providers](../administration/metadata-providers.md). - **EmulatorJS**: a playable in-browser core exists. See [EmulatorJS Configuration](emulatorjs-config.md). - **Firmware**: platform needs BIOS files for emulation. See [Firmware by Platform](firmware-by-platform.md). @@ -44,8 +44,8 @@ uv run python -m scripts.gen_platforms ## See also -- [Folder Structure](../getting-started/folder-structure.md): how platform slugs map to on-disk folders. -- [Custom Platforms](custom-platforms.md): adding platforms outside the built-in list. -- [Metadata Providers](../administration/metadata-providers.md): provider coverage deep-dive. -- [In-Browser Play](../using/in-browser-play.md): EmulatorJS core catalogue. -- [Firmware by Platform](firmware-by-platform.md): per-platform BIOS requirements. +- [Folder Structure](../getting-started/folder-structure.md): how platform slugs map to on-disk folders +- [Custom Platforms](custom-platforms.md): adding platforms outside the built-in list +- [Metadata Providers](../administration/metadata-providers.md): provider coverage deep-dive +- [In-Browser Play](../using/in-browser-play.md): EmulatorJS core catalogue +- [Firmware by Platform](firmware-by-platform.md): per-platform BIOS requirements diff --git a/docs/reference/configuration-file.md b/docs/reference/configuration-file.md index d7e15183..c660e18d 100644 --- a/docs/reference/configuration-file.md +++ b/docs/reference/configuration-file.md @@ -406,12 +406,12 @@ Everything above is also available from the Library Management page in the web U RomM also ships two pre-built config.yml variants for people coming from existing frontends. Copy them wholesale rather than writing one from scratch: -- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml): Batocera / RetroBat layouts. -- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml): ES-DE layout. +- [`config.batocera-retrobat.yml`](https://github.com/rommapp/romm/blob/master/examples/config.batocera-retrobat.yml): Batocera / RetroBat layouts +- [`config.es-de.example.yml`](https://github.com/rommapp/romm/blob/master/examples/config.es-de.example.yml): ES-DE layout ## Related -- [Folder Structure](../getting-started/folder-structure.md): how the filesystem shape interacts with `config.yml`. -- [Metadata Providers](../administration/metadata-providers.md): per-provider detail for the `scan.priority.*` slugs. -- [Scanning & Watcher](../administration/scanning-and-watcher.md): how `exclude.*` interacts with scan runs. -- [Environment Variables](environment-variables.md): env-var overrides for some of the same knobs. +- [Folder Structure](../getting-started/folder-structure.md): how the filesystem shape interacts with `config.yml` +- [Metadata Providers](../administration/metadata-providers.md): per-provider detail for the `scan.priority.*` slugs +- [Scanning & Watcher](../administration/scanning-and-watcher.md): how `exclude.*` interacts with scan runs +- [Environment Variables](environment-variables.md): env-var overrides for some of the same knobs diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 01716f18..740dd190 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -37,9 +37,9 @@ services: Don't embed `ROMM_AUTH_SECRET_KEY`, DB passwords, or provider API keys directly in a committed compose file. Use: -- A `.env` that's `.gitignore`d. -- Docker secrets (`ROMM_AUTH_SECRET_KEY_FILE` reads from a [mounted file](https://docs.docker.com/compose/how-to/use-secrets/)). -- Your orchestrator's secret store (K8s Secrets, HashiCorp Vault, AWS Secrets Manager). +- A `.env` that's `.gitignore`d +- Docker secrets (`ROMM_AUTH_SECRET_KEY_FILE` reads from a [mounted file](https://docs.docker.com/compose/how-to/use-secrets/)) +- Your orchestrator's secret store (K8s Secrets, HashiCorp Vault, AWS Secrets Manager) ## Essential variables @@ -65,7 +65,7 @@ For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../a ## See also -- [Configuration File](configuration-file.md): everything that lives in `config.yml` rather than env vars. -- [Scheduled Tasks](scheduled-tasks.md): cron-controlling env vars in context. -- [Authentication](../administration/authentication.md): auth-related env vars in narrative form. -- [Metadata Providers](../administration/metadata-providers.md): per-provider credential env vars. +- [Configuration File](configuration-file.md): everything that lives in `config.yml` rather than env vars +- [Scheduled Tasks](scheduled-tasks.md): cron-controlling env vars in context +- [Authentication](../administration/authentication.md): auth-related env vars in narrative form +- [Metadata Providers](../administration/metadata-providers.md): per-provider credential env vars diff --git a/docs/reference/exports.md b/docs/reference/exports.md index 07778dba..c9120d34 100644 --- a/docs/reference/exports.md +++ b/docs/reference/exports.md @@ -55,7 +55,7 @@ Authorization: Bearer Content-Type: application/json { - "platforms": ["snes", "nes"] // optional; empty = all + "platforms": ["snes", "nes"] // empty = all } ``` @@ -128,8 +128,8 @@ Not in 5.0. If you want another format (LaunchBox, Attract-Mode, etc.), open an Exports don't auto-rerun on every metadata edit. Triggers: - **Next scan**: exports are part of scan completion when enabled. -- **Manual trigger** via the API above. -- **Admin → Tasks → Export** (if surfaced in your build). +- **Manual trigger** via the API above +- **Admin → Tasks → Export** (if surfaced in your build) For tight sync between RomM edits and the external frontend, run the export via cron or after major edits. @@ -145,4 +145,4 @@ Both export formats include some IDs (`igdb_id`, `moby_id`, etc.). If you rematc - [Configuration File → `scan.gamelist`](../reference/configuration-file.md#scangamelistexport-new-in-50) - [Configuration File → `scan.pegasus`](../reference/configuration-file.md#scanpegasusexport-new-in-50) -- [Metadata Providers → gamelist.xml importer](../administration/metadata-providers.md): reverse direction. +- [Metadata Providers → gamelist.xml importer](../administration/metadata-providers.md): reverse direction diff --git a/docs/reference/feeds.md b/docs/reference/feeds.md index c72d9ef3..6cb312f4 100644 --- a/docs/reference/feeds.md +++ b/docs/reference/feeds.md @@ -77,10 +77,10 @@ Each feed's exact schema matches what its client expects. Don't call feeds by ac Most feeds filter by file extension automatically: -- **Tinfoil** → `.nsp`, `.xci`, `.nsz`, `.xcz`. -- **pkgi** / **fpkgi** → `.pkg`. -- **Kekatsu** → `.nds`. -- **WebRcade** → any extension the WebRcade emulator supports. +- **Tinfoil** → `.nsp`, `.xci`, `.nsz`, `.xcz` +- **pkgi** / **fpkgi** → `.pkg` +- **Kekatsu** → `.nds` +- **WebRcade** → any extension the WebRcade emulator supports Games not matching the expected extension are silently skipped, not reported as missing. @@ -104,6 +104,6 @@ If you want RomM to expose a feed format it doesn't currently support: ## See also -- [Ecosystem](../ecosystem/index.md): per-client setup guides. -- [Authentication](../administration/authentication.md): auth options affecting feeds. -- [API Reference](../developers/api-reference.md): full endpoint catalogue. +- [Ecosystem](../ecosystem/index.md): per-client setup guides +- [Authentication](../administration/authentication.md): auth options affecting feeds +- [API Reference](../developers/api-reference.md): full endpoint catalogue diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index 23acaf09..db4ad7f7 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -128,6 +128,6 @@ Not the most common pattern. Most deployments use a subdomain (`romm.example.com ## See also -- [Reverse Proxy](../install/reverse-proxy.md): passthrough recipes per proxy. -- [Environment Variables](environment-variables.md): including `ROMM_PORT`, `ROMM_BASE_PATH`, `ROMM_BASE_URL`. -- [Architecture](../developers/architecture.md): how the port layout fits into the bigger picture. +- [Reverse Proxy](../install/reverse-proxy.md): passthrough recipes per proxy +- [Environment Variables](environment-variables.md): including `ROMM_PORT`, `ROMM_BASE_PATH`, `ROMM_BASE_URL` +- [Architecture](../developers/architecture.md): how the port layout fits into the bigger picture diff --git a/docs/reference/scheduled-tasks.md b/docs/reference/scheduled-tasks.md index 345fbdaa..11850716 100644 --- a/docs/reference/scheduled-tasks.md +++ b/docs/reference/scheduled-tasks.md @@ -7,9 +7,9 @@ description: Every background task RomM runs, with default schedule, env var, an RomM runs background work via **RQ** (Redis Queue). Tasks fall into three categories: -- **Scheduled**: cron-driven, fire on their own. -- **Watcher**: triggered by filesystem events. -- **Manual**: admin-triggered from the UI or API. +- **Scheduled**: cron-driven, fire on their own +- **Watcher**: triggered by filesystem events +- **Manual**: admin-triggered from the UI or API This page is the **lookup reference**: every task, its cron default, its env var, and a one-line purpose. For the narrative (how to tune cadence, how to trigger manually, how to monitor), see [Administration → Scheduled Tasks](../administration/scheduled-tasks.md). @@ -27,10 +27,10 @@ minute hour day-of-month month day-of-week Examples: -- `0 3 * * *`: 3 AM daily. -- `0 */6 * * *`: every 6 hours. -- `*/30 * * * *`: every 30 minutes. -- `0 2 * * 0`: 2 AM every Sunday. +- `0 3 * * *`: 3 AM daily +- `0 */6 * * *`: every 6 hours +- `*/30 * * * *`: every 30 minutes +- `0 2 * * 0`: 2 AM every Sunday Set the matching env var from the table above, restart the container, and the scheduler picks up the new cadence on startup. @@ -38,7 +38,7 @@ Set the matching env var from the table above, restart the container, and the sc Two approaches: -- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`). Cleanest. +- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`). Cleanest - **Cron-it-off.** Set the cron to a moment that effectively never fires. `0 0 31 2 *` (Feb 31st) works. ## Where the table comes from @@ -47,7 +47,7 @@ Generated by `scripts/gen_scheduled_tasks.py` against the task registry at the S ## See also -- [Administration → Scheduled Tasks](../administration/scheduled-tasks.md): full narrative, per-task purpose, monitoring, tuning. -- [Environment Variables](environment-variables.md): all task-related env vars in context. -- [Scanning & Watcher](../administration/scanning-and-watcher.md): the scan and watcher tasks in depth. -- [SSH Sync](../administration/ssh-sync.md): the push-pull sync task in depth. +- [Administration → Scheduled Tasks](../administration/scheduled-tasks.md): full narrative, per-task purpose, monitoring, tuning +- [Environment Variables](environment-variables.md): all task-related env vars in context +- [Scanning & Watcher](../administration/scanning-and-watcher.md): the scan and watcher tasks in depth +- [SSH Sync](../administration/ssh-sync.md): the push-pull sync task in depth diff --git a/docs/releases/index.md b/docs/releases/index.md index 1aa23f5f..3d6e85d5 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -13,7 +13,7 @@ RomM's current stable is [`5.0.0`](changelog.md). Read [Upgrading to 5.0](upgrad RomM follows **SemVer** for breaking changes and **CalVer-ish** for cadence: major versions are planned milestones, not scheduled. -- **Patch releases** (`5.0.1`, `5.0.2`): bug fixes only. Safe to pull automatically. No migration. +- **Patch releases** (`5.0.1`, `5.0.2`): bug fixes only. Safe to pull automatically. No migration - **Minor releases** (`5.1.0`, `5.2.0`): additive features. Schema may migrate automatically via Alembic but no action required beyond reading the release notes. - **Major releases** (`5.0.0`, `6.0.0`): breaking changes. Read the migration guide before upgrading. @@ -34,7 +34,7 @@ Registries: Docker Hub (`rommapp/romm`) and GitHub Container Registry (`ghcr.io/ The docs site is versioned via [mike](https://github.com/jimporter/mike). Every major RomM release gets its own docs tree, accessible from the version switcher top-left: - `docs.romm.app/latest/` → 5.x (this is what you're reading). -- `docs.romm.app/4.8/` → frozen 4.x docs. +- `docs.romm.app/4.8/` → frozen 4.x docs - Older majors may remain accessible for a while. See the support window below. ## Support window @@ -52,14 +52,14 @@ Older frozen docs are retained for 12 months after the major's support window en ## Migration guides -- **[Upgrading to 5.0](upgrading-to-5.0.md):** 4.x → 5.0. Required reading. -- **[Upgrading to 3.0](upgrading-to-3.0.md):** 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical but kept for 2.x migrators. +- **[Upgrading to 5.0](upgrading-to-5.0.md):** 4.x → 5.0. Required reading +- **[Upgrading to 3.0](upgrading-to-3.0.md):** 2.x → 3.0. SQLite drop, auth requirement, Redis built-in, config folder mount. Historical but kept for 2.x migrators ## Where releases are announced -- [GitHub Releases](https://github.com/rommapp/romm/releases): authoritative changelog, tag-by-tag. -- [Discord](https://discord.gg/romm) `#announcements`: release pings. -- [Changelog](changelog.md): human-readable per-release summary. +- [GitHub Releases](https://github.com/rommapp/romm/releases): authoritative changelog, tag-by-tag +- [Discord](https://discord.gg/romm) `#announcements`: release pings +- [Changelog](changelog.md): human-readable per-release summary ## Upgrade protocol diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index 7f0e60c7..d66ea1b4 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -66,9 +66,9 @@ The `OIDC_REDIRECT_URI` in RomM's env doesn't **exactly** match what's registere Check for: - **Trailing slashes**: `/api/oauth/openid` vs `/api/oauth/openid/` are different to the IdP. -- **Scheme**: `http://` vs `https://`. -- **Host**: `romm.example.com` vs `www.romm.example.com` vs the bare IP. -- **Port**: implied `80` / `443` on HTTPS vs an explicit port. +- **Scheme**: `http://` vs `https://` +- **Host**: `romm.example.com` vs `www.romm.example.com` vs the bare IP +- **Port**: implied `80` / `443` on HTTPS vs an explicit port Fix: make them identical on both sides. diff --git a/docs/troubleshooting/in-browser-play.md b/docs/troubleshooting/in-browser-play.md index e4d6b5ac..09768240 100644 --- a/docs/troubleshooting/in-browser-play.md +++ b/docs/troubleshooting/in-browser-play.md @@ -29,10 +29,10 @@ The platform needs BIOS files RomM doesn't have. See [Firmware Management](../ad Specific examples: -- PlayStation → `scph1001.bin` (or region equivalents). -- Game Boy Advance → `gba_bios.bin`. -- Saturn → `saturn_bios.bin` + CD BIOS. -- Nintendo DS → `firmware.bin` + `bios7.bin` + `bios9.bin`. +- PlayStation → `scph1001.bin` (or region equivalents) +- Game Boy Advance → `gba_bios.bin` +- Saturn → `saturn_bios.bin` + CD BIOS +- Nintendo DS → `firmware.bin` + `bios7.bin` + `bios9.bin` Not sure which files? Error message usually names them. Or check [EmulatorJS Systems docs](https://emulatorjs.org/docs/systems/). @@ -43,7 +43,7 @@ Multi-file firmware: zip them together and upload as a single `firmware.zip`. Em - **Browser needs focus.** Click the emulator canvas once. Some cores need a button press to enumerate gamepads. - **Gamepad not detected.** Chrome sometimes requires a button press on the page before it registers a gamepad. Press something on the pad. - **Conflicting gamepad.** You have two gamepads plugged in and EmulatorJS is talking to the wrong one. Unplug the extras. -- **Keyboard mapping overrides.** In-game Menu → **Controls** → **Reset to Defaults**. +- **Keyboard mapping overrides.** In-game Menu → **Controls** → **Reset to Defaults** ## Audio stutters / desyncs @@ -62,7 +62,7 @@ Multi-file firmware: zip them together and upload as a single `firmware.zip`. Em Different cores use different hotkeys: - **PC keyboard**: often F1, F9, or `~` (tilde). Some cores use ESC. -- **Gamepad**: usually Select + Start simultaneously. +- **Gamepad**: usually Select + Start simultaneously Rebind via Profile → User Interface (the operator-side overrides live in [`emulatorjs.controls`](../reference/configuration-file.md#emulatorjscontrols) in `config.yml`). @@ -78,7 +78,7 @@ See the [MS-DOS platform guide](../platforms/ms-dos.md) for deeper DOS-specific - **"File not Flash"**: confirm the file extension is `.swf`. Ruffle only handles Flash SWF. - **Wrong platform folder.** Ruffle only plays from `flash/` or `browser/` folders. See [Ruffle setup](../using/in-browser-play.md#ruffle). -- **AS3 game crashes**: Ruffle's ActionScript 3 support is in progress. Some games won't work cleanly yet. [Ruffle compatibility list](https://ruffle.rs/#compatibility). +- **AS3 game crashes**: Ruffle's ActionScript 3 support is in progress. Some games won't work cleanly yet. [Ruffle compatibility list](https://ruffle.rs/#compatibility) ## Performance on mobile @@ -107,8 +107,8 @@ In-browser emulation is CPU-heavy. Mobile tips: ## Still stuck -- `docker logs romm | grep -i emulator`: server-side clues. -- Browser devtools Console: client-side clues. +- `docker logs romm | grep -i emulator`: server-side clues +- Browser devtools Console: client-side clues - [Discord](https://discord.gg/romm) `#help`: include the ROM file, the core you tried, the exact error. For Netplay-specific issues: [Netplay Troubleshooting](netplay.md). diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md index 45d5800f..2d4aeebc 100644 --- a/docs/troubleshooting/index.md +++ b/docs/troubleshooting/index.md @@ -17,33 +17,33 @@ Something's broken. Start with the symptom that best matches: ### Login issues -- Getting 403s → [Authentication Troubleshooting](authentication.md). -- OIDC redirect fails → [Authentication Troubleshooting → OIDC](authentication.md#oidc). +- Getting 403s → [Authentication Troubleshooting](authentication.md) +- OIDC redirect fails → [Authentication Troubleshooting → OIDC](authentication.md#oidc) - Forgot the admin password → reset via DB or a fresh admin invite from another admin account. ### Scanning problems -- Scan ends immediately, no platforms found → [Scanning Troubleshooting](scanning.md). +- Scan ends immediately, no platforms found → [Scanning Troubleshooting](scanning.md) - Platform not detected → check folder slug matches [Supported Platforms](../platforms/supported-platforms.md) or add a `system.platforms` binding in [`config.yml`](../reference/configuration-file.md). -- Scan times out → [Scanning Troubleshooting](scanning.md). +- Scan times out → [Scanning Troubleshooting](scanning.md) ### In-browser play -- EmulatorJS won't load / cores 404 → [In-Browser Play Troubleshooting](in-browser-play.md). Probably the slim image without `ENABLE_EMULATORJS` set. -- Netplay doesn't connect → [Netplay Troubleshooting](netplay.md). Almost always NAT / missing ICE servers. +- EmulatorJS won't load / cores 404 → [In-Browser Play Troubleshooting](in-browser-play.md). Probably the slim image without `ENABLE_EMULATORJS` set +- Netplay doesn't connect → [Netplay Troubleshooting](netplay.md). Almost always NAT / missing ICE servers ### Device sync -- Grout / Argosy / DeckRommSync not syncing → [Device Sync Troubleshooting](sync.md). +- Grout / Argosy / DeckRommSync not syncing → [Device Sync Troubleshooting](sync.md) ### Platform-specific -- [Synology Troubleshooting](synology.md): permission errors, DSM gotchas. -- [Kubernetes Troubleshooting](kubernetes.md): the `enableServiceLinks` fix and related. -- [Miscellaneous Troubleshooting](miscellaneous.md): everything else. +- [Synology Troubleshooting](synology.md): permission errors, DSM gotchas +- [Kubernetes Troubleshooting](kubernetes.md): the `enableServiceLinks` fix and related +- [Miscellaneous Troubleshooting](miscellaneous.md): everything else ## Still stuck - [GitHub issues](https://github.com/rommapp/romm/issues): search first, then open if you've got a reproducible bug. -- [Discord](https://discord.gg/romm): `#help` channel, staffed by community + maintainers. -- Logs to include when asking for help: `docker logs romm` (redact secrets), your RomM version (top of the About modal in the profile drawer), and the exact steps you took. +- [Discord](https://discord.gg/romm): `#help` channel, staffed by community + maintainers +- Logs to include when asking for help: `docker logs romm` (redact secrets), your RomM version (top of the About modal in the profile drawer), and the exact steps you took diff --git a/docs/troubleshooting/kubernetes.md b/docs/troubleshooting/kubernetes.md index e9170f02..c6a4137e 100644 --- a/docs/troubleshooting/kubernetes.md +++ b/docs/troubleshooting/kubernetes.md @@ -126,6 +126,6 @@ Or disable hashing on the Scan page to cut memory use by ~80% (you lose RetroAch ## Still stuck -- Full install reference: [Kubernetes](../install/kubernetes.md). -- [Discord](https://discord.gg/romm) `#kubernetes` channel. +- Full install reference: [Kubernetes](../install/kubernetes.md) +- [Discord](https://discord.gg/romm) `#kubernetes` channel - Include your ingress controller, storage class, and the exact error from `kubectl logs` when asking for help. diff --git a/docs/troubleshooting/miscellaneous.md b/docs/troubleshooting/miscellaneous.md index aa45e232..84b56717 100644 --- a/docs/troubleshooting/miscellaneous.md +++ b/docs/troubleshooting/miscellaneous.md @@ -92,6 +92,6 @@ For 4.x → 5.0 specifically, read [Upgrading to 5.0](../releases/upgrading-to-5 ## It's not listed here -- [Discord](https://discord.gg/romm) `#help`: active, friendly, fast. -- [GitHub Issues](https://github.com/rommapp/romm/issues): for reproducible bugs. +- [Discord](https://discord.gg/romm) `#help`: active, friendly, fast +- [GitHub Issues](https://github.com/rommapp/romm/issues): for reproducible bugs - Include your RomM version, deployment method, logs (secrets redacted), and exact repro steps. diff --git a/docs/troubleshooting/netplay.md b/docs/troubleshooting/netplay.md index 700b75ee..940960d5 100644 --- a/docs/troubleshooting/netplay.md +++ b/docs/troubleshooting/netplay.md @@ -67,19 +67,19 @@ If you want your session back, the host recreates the room and other players rej Netplay switches EmulatorJS to nightly-CDN assets. Sometimes the nightly is temporarily broken. -- **Temporary**: usually self-heals within a day. +- **Temporary**: usually self-heals within a day - **Workaround**: operator can disable Netplay (`emulatorjs.netplay.enabled: false`) to go back to stable local assets. See [In-Browser Play → Netplay](../using/netplay.md#known-caveat-nightly-cdn). ## Still stuck -- `docker logs romm | grep -i netplay`: server-side log. -- Browser devtools Console: client-side log. +- `docker logs romm | grep -i netplay`: server-side log +- Browser devtools Console: client-side log - `chrome://webrtc-internals`: live ICE / WebRTC stats on Chrome. Shows exactly where ICE is failing. -- [Discord](https://discord.gg/romm) `#help` with both sides' error text. +- [Discord](https://discord.gg/romm) `#help` with both sides' error text ## See also -- [Using → Netplay](../using/netplay.md): how Netplay works and how to configure it. -- [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50): full config schema. +- [Using → Netplay](../using/netplay.md): how Netplay works and how to configure it +- [Configuration File → `emulatorjs.netplay`](../reference/configuration-file.md#emulatorjsnetplay-new-in-50): full config schema diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md index ffc3ee94..09773a89 100644 --- a/docs/troubleshooting/sync.md +++ b/docs/troubleshooting/sync.md @@ -7,7 +7,7 @@ description: Diagnose push-pull sync, Client API Token pairing, and companion-ap Device Sync covers two distinct things: -- **API sync** (HTTPS, token-auth): used by most companion apps. Argosy, Playnite, RommBrowser, mobile apps. +- **API sync** (HTTPS, token-auth): used by most companion apps. Argosy, Playnite, RommBrowser, mobile apps - **SSH sync** (push-pull task): used by handhelds. Grout on muOS, DeckRommSync on Deck, etc. Troubleshooting paths differ. @@ -23,7 +23,7 @@ Troubleshooting paths differ. ### App can't reach RomM -- **URL wrong.** Double-check what you entered in the app config. `https://romm.example.com`, not `https://romm.example.com/api` (the app appends the API path). +- **URL wrong.** Double-check what you entered in the app config. `https://romm.example.com`, not `https://romm.example.com/api` (the app appends the API path) - **TLS cert issues.** Self-signed certs cause problems. Use a proper cert (Let's Encrypt through your reverse proxy) or configure the app to skip cert validation (not recommended). - **Firewall.** From the device, `curl https://romm.example.com/api/heartbeat`. Should return JSON. If not, network path is blocked. @@ -94,7 +94,7 @@ And verify the Docker bind mount isn't forcing different perms (ro is fine but m Play sessions from a companion app (time-played tracking, Continue Playing ribbon): - **Not appearing in RomM**: app isn't POSTing to `/api/play-sessions`. Check app's logs. -- **Duplicate sessions**: app is re-posting on sync. Known edge case for some companion apps, usually harmless. +- **Duplicate sessions**: app is re-posting on sync. Known edge case for some companion apps, usually harmless - **Times look wrong**: timezone mismatch between device and RomM. RomM stores UTC, and display converts to user TZ. ## Devices panel says "offline" @@ -103,7 +103,7 @@ RomM sees no heartbeat from the device. Either: - Device is genuinely off. - Device's companion app has stopped phoning home. Check the app's logs / relaunch it. -- RomM last-seen threshold elapsed. Devices show online for 1 hour after last heartbeat. After that, offline. +- RomM last-seen threshold elapsed. Devices show online for 1 hour after last heartbeat. After that, offline ## Smart collection / filter isn't updating after sync @@ -113,14 +113,14 @@ Admins can force a re-eval via Administration → Tasks → Refresh Smart Collec ## Still stuck -- **API sync**: `docker logs romm | grep -iE 'sync|device|token'`. -- **SSH sync**: `docker logs romm | grep -iE 'push_pull|ssh'`. +- **API sync**: `docker logs romm | grep -iE 'sync|device|token'` +- **SSH sync**: `docker logs romm | grep -iE 'push_pull|ssh'` - Device-side logs from the companion app. Each app differs. - [Discord](https://discord.gg/romm) `#help`: include the app name, its version, and the RomM log lines. ## See also -- [Client API Tokens](../ecosystem/client-api-tokens.md): token + pairing flow reference. -- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level protocol details. -- [SSH Sync](../administration/ssh-sync.md): server-side SSH sync config. -- [Companion apps](../ecosystem/community-apps.md): list of integrations and their status. +- [Client API Tokens](../ecosystem/client-api-tokens.md): token + pairing flow reference +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level protocol details +- [SSH Sync](../administration/ssh-sync.md): server-side SSH sync config +- [Companion apps](../ecosystem/community-apps.md): list of integrations and their status diff --git a/docs/using/account-and-profile.md b/docs/using/account-and-profile.md index c6ed2a4b..580c50ef 100644 --- a/docs/using/account-and-profile.md +++ b/docs/using/account-and-profile.md @@ -36,15 +36,15 @@ If you're an OIDC user and want RomM to show your `preferred_username` from the Long-lived API tokens for companion apps, scripts, and integrations. Each token is scoped to a subset of your user's scopes, and optionally expires. -See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md); this section is just the "how I create one from the UI" version. +See the full spec in [Client API Tokens](../ecosystem/client-api-tokens.md), as this section is just the "how I create one from the UI" version. ### Creating a token 1. **Profile → Client API Tokens → + New Token**. 2. Pick: - - **Name**: descriptive (e.g. "Grout on my RG35XX"). + - **Name**: descriptive (e.g. "Grout on my RG35XX") - **Scopes**: which permissions to include. Default is read-only, so tick write scopes deliberately. - - **Expiry**: optional, blank = never expires. + - **Expiry**: optional, blank = never expires 3. **Create**. 4. The token appears **once**, so copy it immediately because you can't retrieve it later. @@ -140,12 +140,12 @@ If you signed in via OIDC: Lives on a separate page (**Profile → User Interface**), not here. Covers: -- [Language](languages.md): 19 locales. -- Theme (Dark / Light / Auto). -- Home dashboard ribbons. -- Collection types (toggle [virtual collections](virtual-collections.md) dimensions). -- Game card layout (cover style, 3D tilt, density). -- Full-screen on launch. +- [Language](languages.md): 19 locales +- Theme (Dark / Light / Auto) +- Home dashboard ribbons +- Collection types (toggle [virtual collections](virtual-collections.md) dimensions) +- Game card layout (cover style, 3D tilt, density) +- Full-screen on launch See those pages for detail. diff --git a/docs/using/collections.md b/docs/using/collections.md index c5067ad3..a6dcd203 100644 --- a/docs/using/collections.md +++ b/docs/using/collections.md @@ -9,8 +9,8 @@ A **collection** is a named group of ROMs you curate by hand. Add or remove game RomM has three collection types. This page covers **standard** collections. For the other two: -- [Smart Collections](smart-collections.md): rule-based, auto-populating. -- [Virtual Collections](virtual-collections.md): auto-generated by RomM (by genre, developer, year, tags). +- [Smart Collections](smart-collections.md): rule-based, auto-populating +- [Virtual Collections](virtual-collections.md): auto-generated by RomM (by genre, developer, year, tags) ## Creating a collection @@ -21,7 +21,7 @@ Two ways: 1. Click **Collections** in the menu bar → **+ New Collection**. 2. Pick a name, optional description, optional cover image. 3. Set **visibility**: - - **Private**: only you see it. + - **Private**: only you see it - **Public**: any signed-in user on this instance sees it. Needs `collections.write` scope to toggle (Editor or Admin). 4. Save. The collection is created empty. @@ -53,10 +53,10 @@ Open the collection → click the collection drawer icon → **Edit**. Change: -- Name. -- Description. -- Cover image. -- Visibility (Private / Public). +- Name +- Description +- Cover image +- Visibility (Private / Public) ## Sharing diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md index d4dcdd36..c6253bd7 100644 --- a/docs/using/console-mode.md +++ b/docs/using/console-mode.md @@ -19,7 +19,7 @@ New in 5.0. If you're on a desktop with a keyboard and mouse, you probably don't Three ways: -1. **Menu bar → Console icon** (looks like a small controller). +1. **Menu bar → Console icon** (looks like a small controller) 2. Type `/console` after your RomM URL: `https://romm.example.com/console`. 3. Set the default view to Console Mode in **Profile → User Interface → Default view**. This signs you in straight into `/console` every time. @@ -93,19 +93,19 @@ Console Mode uses its own color palette: higher contrast, bigger typography. It Most of the main UI's features are available in Console Mode: -- ✓ Browse library, platforms, collections. -- ✓ Search and filters. -- ✓ Play (launches [In-Browser Play](in-browser-play.md) or shows a download prompt if the platform isn't browser-playable). -- ✓ Saves and states: upload, select, delete. -- ✓ Smart and virtual collections. -- ✓ RetroAchievements progression display. +- ✓ Browse library, platforms, collections +- ✓ Search and filters +- ✓ Play (launches [In-Browser Play](in-browser-play.md) or shows a download prompt if the platform isn't browser-playable) +- ✓ Saves and states: upload, select, delete +- ✓ Smart and virtual collections +- ✓ RetroAchievements progression display Not yet in Console Mode (still use the main UI): -- ROM editing (match, edit metadata). -- Scanning. -- Administration (users, tasks, stats). -- ROM Patcher. +- ROM editing (match, edit metadata) +- Scanning +- Administration (users, tasks, stats) +- ROM Patcher ## Configuring @@ -147,8 +147,8 @@ For multi-disc games, Console Mode asks which disc to boot before launching. For Running on muOS / Batocera / Knulli / a Steam Deck? Consider: -- **[Grout](../ecosystem/grout.md)**: official handheld companion that syncs saves/states to/from the device. -- **[Argosy Launcher](../ecosystem/argosy.md)**: Android handhelds that can run a browser but want a native-feeling app. +- **[Grout](../ecosystem/grout.md)**: official handheld companion that syncs saves/states to/from the device +- **[Argosy Launcher](../ecosystem/argosy.md)**: Android handhelds that can run a browser but want a native-feeling app Both use [Client API Tokens](../ecosystem/client-api-tokens.md) for auth. diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md index 5f66db92..16fce1eb 100644 --- a/docs/using/in-browser-play.md +++ b/docs/using/in-browser-play.md @@ -8,7 +8,7 @@ description: Play games directly in your browser with EmulatorJS and Ruffle: con RomM ships with two in-browser emulators: - **EmulatorJS**: RetroArch cores compiled to WebAssembly. Covers NES, SNES, Genesis, N64, PSX, PSP, Saturn, and 30+ other platforms. -- **Ruffle**: Flash / Shockwave emulator for browser games. +- **Ruffle**: Flash / Shockwave emulator for browser games Hit the **Play** button on any supported game, and the emulator loads full-screen in a new page. Same UI on desktop, mobile, and Console Mode. @@ -85,7 +85,7 @@ RomM integrates with EmulatorJS so save-files and save-states are loaded and sav - **Before launch**: if multiple saves exist, RomM asks which to load. - **During play**: any in-emulator save (SRAM flush) or state creation is written straight back to RomM's storage. -- **After quit**: everything is already persisted. No manual download. +- **After quit**: everything is already persisted. No manual download Full details: [Saves & States](saves-and-states.md). @@ -99,8 +99,8 @@ During play: in-game Menu → **Screenshot**. Saved to your personal screenshot ### Fullscreen -- **F11**: browser fullscreen. -- In-game Menu → **Fullscreen** button: same thing. +- **F11**: browser fullscreen +- In-game Menu → **Fullscreen** button: same thing ### Multi-disc games @@ -148,7 +148,7 @@ If you enable the [Flashpoint](../administration/metadata-providers.md#flashpoin ## Troubleshooting -- **Game won't boot**: check firmware is uploaded for platforms that need it. `docker logs romm | grep -i emulator` for server-side hints. +- **Game won't boot**: check firmware is uploaded for platforms that need it. `docker logs romm | grep -i emulator` for server-side hints - **Black screen, no audio**: core is incompatible with your browser. Try a different browser (Chrome / Firefox are most tested) or a different core via in-game Menu → **Core**. - **Controls do nothing**: browser needs focus. Click once on the emulator canvas. Some cores need a button press to enumerate gamepads. - **Netplay "failed to start game"**: see [Netplay troubleshooting](../troubleshooting/netplay.md). diff --git a/docs/using/index.md b/docs/using/index.md index 05342bd8..e72744d7 100644 --- a/docs/using/index.md +++ b/docs/using/index.md @@ -11,37 +11,37 @@ Admins: see [Administration](../administration/index.md) for operator tasks. ## Browse and play -- **[Library](library.md)**: the main UI: home dashboard, platform and game cards, filters, the search bar. -- **[In-Browser Play](in-browser-play.md)**: EmulatorJS for retro consoles, Ruffle for Flash. Save states, cheats, fullscreen. -- **[Downloads](downloads.md)**: single, bulk, QR code, copy link. -- **[Uploads](uploads.md)**: drag-and-drop ROMs, firmware, saves, states, screenshots. +- **[Library](library.md)**: the main UI: home dashboard, platform and game cards, filters, the search bar +- **[In-Browser Play](in-browser-play.md)**: EmulatorJS for retro consoles, Ruffle for Flash. Save states, cheats, fullscreen +- **[Downloads](downloads.md)**: single, bulk, QR code, copy link +- **[Uploads](uploads.md)**: drag-and-drop ROMs, firmware, saves, states, screenshots ## Organise -- **[Collections](collections.md)**: hand-curated lists. -- **[Smart Collections](smart-collections.md)**: rule-based, auto-populating. -- **[Virtual Collections](virtual-collections.md)**: auto-generated by genre, developer, year, tags. +- **[Collections](collections.md)**: hand-curated lists +- **[Smart Collections](smart-collections.md)**: rule-based, auto-populating +- **[Virtual Collections](virtual-collections.md)**: auto-generated by genre, developer, year, tags ## Personal data -- **[Saves & States](saves-and-states.md)**: upload, sync, select before launching. -- **[RetroAchievements](retroachievements.md)**: link your RA account, surface progression. -- **[Account & Profile](account-and-profile.md)**: change your password, email, avatar. API tokens. +- **[Saves & States](saves-and-states.md)**: upload, sync, select before launching +- **[RetroAchievements](retroachievements.md)**: link your RA account, surface progression +- **[Account & Profile](account-and-profile.md)**: change your password, email, avatar. API tokens ## Special modes -- **[Console Mode](console-mode.md)**: `/console` UI for TVs and gamepads. Spatial navigation, SFX, no mouse needed. +- **[Console Mode](console-mode.md)**: `/console` UI for TVs and gamepads. Spatial navigation, SFX, no mouse needed - **[Install as PWA](pwa.md)**: add RomM to your phone/desktop home screen. -- **[Mobile & TV](mobile-and-tv.md)**: device-specific usage patterns. +- **[Mobile & TV](mobile-and-tv.md)**: device-specific usage patterns ## Tools - **[ROM Patcher](rom-patcher.md)**: apply IPS/UPS/BPS/PPF and more in-browser. -- **[Netplay](netplay.md)**: EmulatorJS multiplayer sessions. +- **[Netplay](netplay.md)**: EmulatorJS multiplayer sessions ## Settings -- **[Languages](languages.md)**: 19 locales. +- **[Languages](languages.md)**: 19 locales --- diff --git a/docs/using/languages.md b/docs/using/languages.md index 73fb4eac..cea86e09 100644 --- a/docs/using/languages.md +++ b/docs/using/languages.md @@ -33,15 +33,15 @@ Two more ship as work-in-progress with partial coverage. Check the language drop ## What gets translated -- All UI strings: menus buttons, labels, tooltips, dialogs. -- Error messages and notifications. -- Help text and inline instructions. +- All UI strings: menus buttons, labels, tooltips, dialogs +- Error messages and notifications +- Help text and inline instructions Not translated: - **Game metadata**: titles, descriptions, genres come from metadata providers. English is usually the source language. IGDB has some localisation but coverage is uneven. -- **Your own content**: collection names, notes, user-uploaded media. -- **Docs site** (what you're reading): English only in 5.0. +- **Your own content**: collection names, notes, user-uploaded media +- **Docs site** (what you're reading): English only in 5.0 ## Missing strings @@ -89,6 +89,6 @@ The docs site is currently English-only. Localising docs is a much bigger commit ## Related -- [User Interface settings](account-and-profile.md): full list of personal UI preferences. +- [User Interface settings](account-and-profile.md): full list of personal UI preferences - [Console Mode](console-mode.md): same locale selection applies. -- [Developers → Translations (i18n)](../developers/i18n.md): contributor guide. +- [Developers → Translations (i18n)](../developers/i18n.md): contributor guide diff --git a/docs/using/library.md b/docs/using/library.md index 83c96ca1..2febfe6f 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -13,10 +13,10 @@ The library is the heart of RomM. This page covers the day-to-day UI: the dashbo Home screen, with several **ribbons** of content: -- **Recently Added**: a carousel of the latest ROMs RomM has indexed. +- **Recently Added**: a carousel of the latest ROMs RomM has indexed - **Continue Playing**: games with an active play session. See [Play Sessions](../getting-started/concepts.md#play-session). - **Platforms**: every platform that has at least one ROM. Cards link into the [platform view](#platform-view). -- **Collections**: your [collections](collections.md), [smart collections](smart-collections.md), and [virtual collections](virtual-collections.md). +- **Collections**: your [collections](collections.md), [smart collections](smart-collections.md), and [virtual collections](virtual-collections.md) Each ribbon can be hidden or shown from **Profile → User Interface**, handy if you prefer a tight dashboard. @@ -24,13 +24,13 @@ Each ribbon can be hidden or shown from **Profile → User Interface**, handy if Visible everywhere, with shortcuts to: -- **Search**: global search across every ROM, metadata field, and tag. -- **Platforms**: drawer listing every platform as a link. -- **Collections**: drawer listing every collection type. -- **Scan**: launches a scan. Permission-gated (Admin/Editor). +- **Search**: global search across every ROM, metadata field, and tag +- **Platforms**: drawer listing every platform as a link +- **Collections**: drawer listing every collection type +- **Scan**: launches a scan. Permission-gated (Admin/Editor) - **Console Mode**: jumps to the `/console` TV/gamepad UI. See [Console Mode](console-mode.md). - **Upload**: permission-gated (Admin/Editor). See [Uploads](uploads.md). -- **Profile**: profile drawer + admin panel. +- **Profile**: profile drawer + admin panel ## Grid/list toggle @@ -42,19 +42,19 @@ Every gallery (platform view, collection view, search results) has a grid-vs-lis Hovering over a game card exposes: -- **Play**: launch in [EmulatorJS or Ruffle](in-browser-play.md), if the platform supports in-browser play. +- **Play**: launch in [EmulatorJS or Ruffle](in-browser-play.md), if the platform supports in-browser play - **Download**: single-file download. See [Downloads](downloads.md) for bulk, QR, and copy-link options. -- **Context menu (…)**: opens the card's action menu. +- **Context menu (…)**: opens the card's action menu Card context menu: ![context menu](https://raw.githubusercontent.com/rommapp/docs/refs/heads/main/docs/resources/usage/ContextMenu.png) -- **Match to metadata agent**: manually rematch against a provider (IGDB, ScreenScraper, etc.). +- **Match to metadata agent**: manually rematch against a provider (IGDB, ScreenScraper, etc.) - **Edit**: change title, platform, or other metadata fields (permission-gated). - **Refresh metadata**: re-query providers and overwrite. -- **Add/remove from Favourites**. -- **Add/remove from Collection**: surfaces your existing collections. +- **Add/remove from Favourites** +- **Add/remove from Collection**: surfaces your existing collections Double-click (or tap) the card to open the [game view](#game-view). @@ -64,13 +64,13 @@ Every gallery has a Filters button in the top right. Filter combinations stack, ### Toggle filters -- **Show Unmatched**: games with no provider match. -- **Show Matched**: the inverse. -- **Show Favourites**: your personal favourites. -- **Show Duplicates**: games appearing more than once. -- **Show Playables**: only games with in-browser play support on this platform. -- **Show Missing**: DB entries whose files are gone. -- **Show Verified**: matched via Hasheous. +- **Show Unmatched**: games with no provider match +- **Show Matched**: the inverse +- **Show Favourites**: your personal favourites +- **Show Duplicates**: games appearing more than once +- **Show Playables**: only games with in-browser play support on this platform +- **Show Missing**: DB entries whose files are gone +- **Show Verified**: matched via Hasheous - **Show RetroAchievements**: games RetroAchievements has achievements for. See [RetroAchievements](retroachievements.md). ### Value filters @@ -78,8 +78,8 @@ Every gallery has a Filters button in the top right. Filter combinations stack, Dropdowns that cross-reference metadata on the visible set: - **Platform**: scope to one platform. -- **Genre / Franchise / Collections / Company / Age Rating / Region / Language**: metadata dimensions. -- **Status**: personal play status (Never Played, Backlogged, Playing, Complete, Hidden). +- **Genre / Franchise / Collections / Company / Age Rating / Region / Language**: metadata dimensions +- **Status**: personal play status (Never Played, Backlogged, Playing, Complete, Hidden) Filters narrow down _what you're currently viewing_. Search first, filter second, and the filter dropdowns only show values present in the search results. @@ -87,9 +87,9 @@ Filters narrow down _what you're currently viewing_. Search first, filter second Click the search icon in the menu bar. Real-time search against: -- Game title. -- Tags (regions, languages, revisions, arbitrary `[]`/`()` tags). -- Metadata fields (genre, developer, publisher, summary). +- Game title +- Tags (regions, languages, revisions, arbitrary `[]`/`()` tags) +- Metadata fields (genre, developer, publisher, summary) Search is platform-aware: type `zelda` and you'll see every matched Zelda across every platform you have. @@ -107,8 +107,8 @@ Returns every game whose metadata matches `zelda` **or** `mario` **or** `sonic`. Two icons next to the search bar: -- **View filters**: same filter panel as above. -- **New collection from results**: saves the current search + filter state as a [standard collection](collections.md). +- **View filters**: same filter panel as above +- **New collection from results**: saves the current search + filter state as a [standard collection](collections.md) ## Platform view @@ -133,11 +133,11 @@ Click (or tap) a game. The game view has two halves: ### Left: hero panel -- Cover art (or `vanilla-tilt` 3D hover if enabled in UI settings). -- **Play** button (if playable). -- **Download** button + menu (Copy Link, QR Code, Bulk Download). +- Cover art (or `vanilla-tilt` 3D hover if enabled in UI settings) +- **Play** button (if playable) +- **Download** button + menu (Copy Link, QR Code, Bulk Download) - **Upload ROM**: replace / add a new version. -- Context menu: Match, Edit, Match rehash, Delete, etc. (permission-gated). +- Context menu: Match, Edit, Match rehash, Delete, etc. (permission-gated) ### Right: tabs @@ -145,14 +145,14 @@ Which tabs appear depends on your metadata providers: - **Details**: title, description, release date, genres, developer/publisher, regions, rating, matched providers. Filterable metadata surfaces here. - **Game Data**: save files, save states, screenshots. Per-user. Upload, download, and delete. See [Saves & States](saves-and-states.md). -- **Personal**: your data on this game: status (Never Played / Backlogged / Playing / Complete / Hidden), rating, difficulty, percent complete, and **playtime** (accumulated as you play via the [in-browser player](in-browser-play.md) or companion apps that post play sessions). Stored per-user. -- **Notes**: Markdown-formatted notes on the game. Per-user. Set a note to **public** to share with other users on the instance. You can have multiple notes per game (e.g. "walkthrough tips", "save states reference", "secret moves"). Edited via a Markdown editor with preview pane. -- **Manual**: PDF viewer if you have a manual for this game. -- **Time to Beat**: [HowLongToBeat](../administration/metadata-providers.md#howlongtobeat) data if enabled. -- **Screenshots**: provider-fetched + user-uploaded screenshots. -- **Achievements**: [RetroAchievements](retroachievements.md) progression if you've linked your RA account. -- **Related Games**: similar titles from IGDB. -- **Additional Content**: DLCs, patches, hacks, mods, translations found inside the multi-file folder. +- **Personal**: your data on this game: status (Never Played / Backlogged / Playing / Complete / Hidden), rating, difficulty, percent complete, and **playtime** (accumulated as you play via the [in-browser player](in-browser-play.md) or companion apps that post play sessions). Stored per-user +- **Notes**: Markdown-formatted notes on the game. Per-user. Set a note to **public** to share with other users on the instance. You can have multiple notes per game (e.g. "walkthrough tips", "save states reference", "secret moves"). Edited via a Markdown editor with preview pane +- **Manual**: PDF viewer if you have a manual for this game +- **Time to Beat**: [HowLongToBeat](../administration/metadata-providers.md#howlongtobeat) data if enabled +- **Screenshots**: provider-fetched + user-uploaded screenshots +- **Achievements**: [RetroAchievements](retroachievements.md) progression if you've linked your RA account +- **Related Games**: similar titles from IGDB +- **Additional Content**: DLCs, patches, hacks, mods, translations found inside the multi-file folder ## Keyboard shortcuts diff --git a/docs/using/mobile-and-tv.md b/docs/using/mobile-and-tv.md index 5adcd0b4..70984cc5 100644 --- a/docs/using/mobile-and-tv.md +++ b/docs/using/mobile-and-tv.md @@ -13,22 +13,22 @@ RomM is a web app. Anything with a modern browser can use it: phones, tablets, h Install RomM as a [Progressive Web App](pwa.md): feels native, launches from the home screen, no app store. Recommended for most mobile users. -- Good for: browsing, managing your library, downloading ROMs, in-browser play on devices with enough horsepower. -- Limited by: no push notifications (yet), no OS-level file pickers for ROM uploads (use the web UI instead), Safari quirks on iOS. +- Good for: browsing, managing your library, downloading ROMs, in-browser play on devices with enough horsepower +- Limited by: no push notifications (yet), no OS-level file pickers for ROM uploads (use the web UI instead), Safari quirks on iOS ### Option B: a native app [Community-maintained mobile apps](../ecosystem/community-apps.md) exist: -- **Argosy Launcher** (Android): official first-party. ROM syncing and launching. -- **romm-ios-app** / **romm-mobile**: community iOS and Android clients. +- **Argosy Launcher** (Android): official first-party. ROM syncing and launching +- **romm-ios-app** / **romm-mobile**: community iOS and Android clients Native apps give you proper file pickers, OS notifications, and nicer integration with device-level emulators (RetroArch mobile, Delta on iOS, etc.). ### Browser choice - **Android**: Chrome, Firefox, or Samsung Internet. Chrome has best PWA support. -- **iOS**: Safari (the only way to install as PWA on iOS). +- **iOS**: Safari (the only way to install as PWA on iOS) ### Touch vs mouse @@ -64,9 +64,9 @@ Handhelds running custom firmware (muOS, Batocera, Knulli, ArkOS, JELOS, ROCKNIX Recommended companions: -- **[Grout](../ecosystem/grout.md)**: first-party. muOS and NextUI handhelds. -- **[DeckRommSync](../ecosystem/community-apps.md)**: Steam Deck (SteamOS). -- **[SwitchRomM](../ecosystem/community-apps.md)**: Nintendo Switch (homebrew). +- **[Grout](../ecosystem/grout.md)**: first-party. muOS and NextUI handhelds +- **[DeckRommSync](../ecosystem/community-apps.md)**: Steam Deck (SteamOS) +- **[SwitchRomM](../ecosystem/community-apps.md)**: Nintendo Switch (homebrew) All of these use [Client API Tokens](../ecosystem/client-api-tokens.md) for auth and the [Device Sync Protocol](../ecosystem/device-sync-protocol.md) for the actual data transfer. @@ -109,7 +109,7 @@ If you host RomM on a home server and want to reach it from cellular: ## See also -- [Install as PWA](pwa.md). -- [Console Mode](console-mode.md). -- [Integrations & Ecosystem](../ecosystem/index.md): every companion app RomM supports. -- [Community Apps](../ecosystem/community-apps.md): the full list with platform / status flags. +- [Install as PWA](pwa.md) +- [Console Mode](console-mode.md) +- [Integrations & Ecosystem](../ecosystem/index.md): every companion app RomM supports +- [Community Apps](../ecosystem/community-apps.md): the full list with platform / status flags diff --git a/docs/using/netplay.md b/docs/using/netplay.md index 73015f69..29072ae5 100644 --- a/docs/using/netplay.md +++ b/docs/using/netplay.md @@ -15,7 +15,7 @@ Best for 2-player co-op and turn-based games. Not ideal for twitchy fighting gam ## Prerequisites -- EmulatorJS Netplay enabled in `config.yml` (operator-level). +- EmulatorJS Netplay enabled in `config.yml` (operator-level) - ICE servers configured (STUN + TURN). Without them, Netplay only works when all players are on the same LAN. - All players need access to your RomM instance. Netplay doesn't proxy the ROM to people without RomM accounts. @@ -74,7 +74,7 @@ For production Netplay, operators should get a dedicated TURN account (free tier ## Limitations -- **Not all cores support Netplay.** SNES9x, Mupen64Plus, Mednafen PSX, Genesis Plus GX: generally yes. Cores like PPSSPP or dosbox-pure: no. +- **Not all cores support Netplay.** SNES9x, Mupen64Plus, Mednafen PSX, Genesis Plus GX: generally yes. Cores like PPSSPP or dosbox-pure: no - **Frame-perfect fighting isn't realistic.** Netplay is for casual co-op, not tournament-level fighting games. Use something like [FightCade](https://www.fightcade.com/) for that. - **All players need RomM access.** There's no "join a friend's game without an account". Guests need at least a Viewer account on your instance. - **RTC over TURN uses real bandwidth.** Hosting a 4-player N64 session over TURN can saturate a modest uplink. Prefer STUN where possible. @@ -95,7 +95,7 @@ Netplay room data is short-lived, with no persistent record on RomM beyond the [ ## Troubleshooting -- **"Failed to start game"**: Netplay server-side config is broken. Operator: check `docker logs romm | grep -i netplay`. Usually a misconfigured ICE server URL. +- **"Failed to start game"**: Netplay server-side config is broken. Operator: check `docker logs romm | grep -i netplay`. Usually a misconfigured ICE server URL - **Other players can't see my room**: either they're not on your RomM (shouldn't happen if they have accounts) or the WebSocket connection is broken. See [WebSocket Troubleshooting](../troubleshooting/authentication.md#400-bad-request-on-the-websocket-endpoint). - **Game plays fine locally but Netplay glitches**: network round-trip is too high. Test with Players on the same LAN first, then add TURN and re-test from outside. - **Audio desync**: known WebRTC issue. Try a different browser (Chrome is most-tested), or restart the session. diff --git a/docs/using/pwa.md b/docs/using/pwa.md index 8ccae03c..8a11639b 100644 --- a/docs/using/pwa.md +++ b/docs/using/pwa.md @@ -11,9 +11,9 @@ New in 5.0. ## Why install? -- **Looks and feels native**: icon on your home screen, fullscreen by default. +- **Looks and feels native**: icon on your home screen, fullscreen by default - **Faster startup**: the service worker caches the shell. -- **Mobile-friendly**: no browser address bar eating vertical space. +- **Mobile-friendly**: no browser address bar eating vertical space - **Offline-ish**: you can open the app without a network, though anything that actually fetches data still needs the server. ## Install on Android @@ -43,7 +43,7 @@ The icon appears on your home screen. Launching opens RomM in a standalone Safar iOS limitations: -- No push notifications. +- No push notifications - Service worker caching is more aggressive / unpredictable than on Android. - The browser UI is fully gone, leaving only gesture-based navigation. @@ -75,7 +75,7 @@ Same standalone window treatment as Chrome. - **Android**: long-press the icon → **Uninstall** (or **Remove from Home screen**). - **iOS**: long-press → **Remove App**. -- **Desktop**: inside the installed PWA window → three-dot menu → **Uninstall RomM**. +- **Desktop**: inside the installed PWA window → three-dot menu → **Uninstall RomM** Uninstalling just removes the shortcut and cached shell. Nothing server-side is touched. @@ -101,7 +101,7 @@ If a stale shell is causing issues (e.g. seeing old UI after an upgrade), force- PWA + [Console Mode](console-mode.md) is a powerful combo: - Install the PWA on a TV-attached Android box, then set default view to `/console`. -- Launching the app goes straight to a gamepad-friendly library. Feels like a console. +- Launching the app goes straight to a gamepad-friendly library. Feels like a console ## Troubleshooting diff --git a/docs/using/retroachievements.md b/docs/using/retroachievements.md index 7b6f54c3..8572c84d 100644 --- a/docs/using/retroachievements.md +++ b/docs/using/retroachievements.md @@ -31,11 +31,11 @@ From this point on, RomM auto-syncs every user's progression via the nightly Ret Per-game breakdown: -- Total achievements authored. -- How many you've unlocked. -- Points earned / total points available. -- Hardcore vs softcore totals (if applicable). -- List of specific achievements with icons and descriptions. +- Total achievements authored +- How many you've unlocked +- Points earned / total points available +- Hardcore vs softcore totals (if applicable) +- List of specific achievements with icons and descriptions ### Game cards @@ -55,7 +55,7 @@ A small badge appears on games where RA achievements exist, regardless of whethe RetroAchievements distinguishes two play modes: - **Softcore**: save states allowed. Achievements still count. -- **Hardcore**: no save states. More points per achievement, flagged separately. +- **Hardcore**: no save states. More points per achievement, flagged separately RomM doesn't enforce hardcore. You toggle it per-game in RetroAchievements-capable cores. Loading a save state during a hardcore run will silently drop hardcore crediting on the RA server side. You'll still see the achievement but marked as softcore. diff --git a/docs/using/rom-patcher.md b/docs/using/rom-patcher.md index 6723139f..3eb7a3d8 100644 --- a/docs/using/rom-patcher.md +++ b/docs/using/rom-patcher.md @@ -36,8 +36,8 @@ Menu bar → **Patcher** icon (a wrench). Admins and Editors can also reach it f 3. **Pick the target platform**: usually auto-detected from the ROM. 4. **Choose output**: - **Download patched ROM**: saves a file locally. RomM keeps neither the original nor the result. - - **Save to library**: adds the patched ROM as a new entry in your library, alongside the original. - - Optional **filename**: customise the output name. Defaults to ` [patched].`. + - **Save to library**: adds the patched ROM as a new entry in your library, alongside the original + - Optional **filename**: customise the output name. Defaults to ` [patched].` 5. **Apply**. Everything runs client-side in the browser. The ROM never gets uploaded to the RomM server during patching (if you picked Download), so nothing leaves your machine. diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md index d5e1a86b..af80fc3a 100644 --- a/docs/using/saves-and-states.md +++ b/docs/using/saves-and-states.md @@ -16,8 +16,8 @@ Both are **per-user per-ROM** and stored under `/romm/assets///`. The Game detail page → **Game Data** tab. Separate sub-tabs for: -- **Saves**: all save files for this ROM, with date and emulator. -- **States**: all states, with thumbnail (if one was captured). +- **Saves**: all save files for this ROM, with date and emulator +- **States**: all states, with thumbnail (if one was captured) ## Uploading a save @@ -38,7 +38,7 @@ Same flow under **Upload State**. Optional screenshot attachment. RomM autogener In-game Menu in EmulatorJS: -- **Save**: writes in-game save data back to RomM. Same as the native console's save-to-cartridge flow. +- **Save**: writes in-game save data back to RomM. Same as the native console's save-to-cartridge flow - **Save State** / **Load State**: creates a full memory snapshot, or restores from one. RomM uploads the snapshot automatically. There's no "forgot to upload" step. Everything persists as soon as you do it in-emulator. @@ -47,8 +47,8 @@ There's no "forgot to upload" step. Everything persists as soon as you do it in- If a ROM has multiple saves or states, the pre-launch picker appears before the emulator loads: -- **Save file** dropdown: which save to load on boot. -- **State** dropdown: optional, loads immediately after boot. +- **Save file** dropdown: which save to load on boot +- **State** dropdown: optional, loads immediately after boot [Console Mode](console-mode.md) surfaces the same picker on a larger gamepad-friendly dialog. @@ -70,9 +70,9 @@ For bulk cleanup (e.g. "delete every state for games I've beaten"), use the mult Saves and states can sync to/from registered devices (Grout on muOS, DeckRommSync on a Deck, etc.). Covered in depth in the ecosystem section: -- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level reference. -- [SSH Sync](../administration/ssh-sync.md): operator-side config. -- [Argosy Launcher](../ecosystem/argosy.md) / [Grout](../ecosystem/grout.md): per-app setup. +- [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level reference +- [SSH Sync](../administration/ssh-sync.md): operator-side config +- [Argosy Launcher](../ecosystem/argosy.md) / [Grout](../ecosystem/grout.md): per-app setup From the end-user side: once your device is paired and sync is running, saves made on the device appear in RomM within a couple of sync cycles (default: 15 minutes). Conflicts (same ROM saved on two devices between syncs) surface as two separate save entries, so pick which to keep. @@ -82,15 +82,15 @@ From the end-user side: once your device is paired and sync is running, saves ma Save files are usually format-interchangeable across cores for the same platform but not always. -| Platform | Format | Usually-compatible | -| -------------------- | ---------------------- | -------------------------------------------------- | -| NES | `.sav` | Yes, across FCEUmm / Nestopia | -| SNES | `.srm` | Yes, across SNES9x / bsnes | -| Genesis / Mega Drive | `.srm` | Yes | -| Game Boy / GBC / GBA | `.sav` | Yes, across Gambatte / mGBA | +| Platform | Format | Usually-compatible | +| -------------------- | ---------------------- | ------------------------------------------------- | +| NES | `.sav` | Yes, across FCEUmm / Nestopia | +| SNES | `.srm` | Yes, across SNES9x / bsnes | +| Genesis / Mega Drive | `.srm` | Yes | +| Game Boy / GBC / GBA | `.sav` | Yes, across Gambatte / mGBA | | N64 | `.srm`, `.eep`, `.fla` | Yes but per-type: the right file must be uploaded | -| PSX | `.srm` (memory card) | Yes, across Mednafen / PCSX cores | -| Saturn / Dreamcast | Varies | Check core docs | +| PSX | `.srm` (memory card) | Yes, across Mednafen / PCSX cores | +| Saturn / Dreamcast | Varies | Check core docs | If you're moving saves between RomM's EmulatorJS and a stand-alone emulator (RetroArch, Dolphin, PPSSPP), usually works for mainline cores. diff --git a/docs/using/smart-collections.md b/docs/using/smart-collections.md index 6da86d59..8bbee986 100644 --- a/docs/using/smart-collections.md +++ b/docs/using/smart-collections.md @@ -24,38 +24,38 @@ A smart collection is one or more **conditions** joined by **all** (AND) or **an Each condition has three parts: -1. **Field**: what you're matching on. -2. **Operator**: comparison (equals, contains, greater-than, etc.). -3. **Value**: the thing you're comparing against. +1. **Field**: what you're matching on +2. **Operator**: comparison (equals, contains, greater-than, etc.) +3. **Value**: the thing you're comparing against ## Supported fields ### Metadata -- **Platform**: platform slug. -- **Title**: game title (case-insensitive substring match with `contains`). -- **Genre**: IGDB genre tag. -- **Franchise**: game franchise (Mario, Final Fantasy, etc.). -- **Developer**: company that made the game. -- **Publisher**: company that released it. -- **Release Year**: year. -- **Age Rating**: ESRB / PEGI rating. -- **Region**: game region (USA, Japan, Europe, World, etc.). -- **Language**: game language. -- **Rating**: IGDB / ScreenScraper critic score. +- **Platform**: platform slug +- **Title**: game title (case-insensitive substring match with `contains`) +- **Genre**: IGDB genre tag +- **Franchise**: game franchise (Mario, Final Fantasy, etc.) +- **Developer**: company that made the game +- **Publisher**: company that released it +- **Release Year**: year +- **Age Rating**: ESRB / PEGI rating +- **Region**: game region (USA, Japan, Europe, World, etc.) +- **Language**: game language +- **Rating**: IGDB / ScreenScraper critic score ### Personal data -- **Personal Rating**: your per-game rating. -- **Status**: Never Played / Backlogged / Playing / Complete / Hidden. -- **Playtime**: accumulated play time (minutes). -- **Favourites**: whether you've favourited it. -- **Has Achievements**: whether the game has [RetroAchievements](retroachievements.md). +- **Personal Rating**: your per-game rating +- **Status**: Never Played / Backlogged / Playing / Complete / Hidden +- **Playtime**: accumulated play time (minutes) +- **Favourites**: whether you've favourited it +- **Has Achievements**: whether the game has [RetroAchievements](retroachievements.md) ### Flags -- **Matched**: has a provider ID. -- **Playable in browser**: supports EmulatorJS / Ruffle. +- **Matched**: has a provider ID +- **Playable in browser**: supports EmulatorJS / Ruffle - **Has Firmware**: required firmware exists in the library. - **Duplicate**: the same game appears twice. @@ -138,7 +138,7 @@ Same as standard collections: removes the definition. ROMs stay in the library. ## Limitations - **No nested smart collections**: a smart collection can't reference another collection as a source. Compose rules directly. -- **Performance**: very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible but mentioned here for completeness. +- **Performance**: very complex rule sets (many conditions, many nested groups) on huge libraries can slow down the gallery load. Usually imperceptible but mentioned here for completeness - **Timezone**: "Release Year" uses UTC, not the user's timezone. Edge-case edge-of-year games might fall on the "wrong" side. ## API diff --git a/docs/using/virtual-collections.md b/docs/using/virtual-collections.md index b1f5df66..6911c586 100644 --- a/docs/using/virtual-collections.md +++ b/docs/using/virtual-collections.md @@ -43,8 +43,8 @@ Disabling a dimension hides all its collections from your drawer and dashboard. Virtual collections show up alongside regular and smart collections in: -- The **Collections** drawer. -- The dashboard's **Collections** ribbon (if enabled). +- The **Collections** drawer +- The dashboard's **Collections** ribbon (if enabled) - The filter panel under "Collection", so you can filter any gallery by virtual collection membership. Open one like any collection: grid/list view, filters, play, download, etc. @@ -75,9 +75,9 @@ Almost always metadata. A game missing from "Virtual Collection: RPG" probably d Genre coverage varies by metadata provider: -- **IGDB**: rich genre data. -- **ScreenScraper**: ok genre data, with some provider-specific labels. -- **Hasheous / PlayMatch** (hash-only): no genre data, because they proxy IGDB. +- **IGDB**: rich genre data +- **ScreenScraper**: ok genre data, with some provider-specific labels +- **Hasheous / PlayMatch** (hash-only): no genre data, because they proxy IGDB If your library is heavily ScreenScraper-matched and you want rich genres, add IGDB and run an **Unmatched** or **Update Metadata** scan. See [Metadata Providers](../administration/metadata-providers.md). From f3af57083bcb69bf9e39835c865af7e245465c90 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 22:50:16 -0400 Subject: [PATCH 043/121] complete authentication --- docs/administration/authentication.md | 36 ++++++++++++-------------- docs/ecosystem/client-api-tokens.md | 2 +- docs/ecosystem/device-sync-protocol.md | 2 +- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index 85b684d5..098fea8b 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -1,19 +1,19 @@ --- title: Authentication -description: Configure how users sign in: sessions, password policy, client tokens, OIDC hooks, and kiosk mode. +description: Configure how users sign in. --- # Authentication -This page is the **operator-side** authentication reference: knobs you turn on the server to control how people sign in. The **client-side** reference ("how do I actually authenticate an API call?") is in [API Authentication](../developers/api-authentication.md). +This page is the **operator-side** authentication reference, the knobs you turn on the server to control how people sign in. The **client-side** reference ("how do I actually authenticate an API call?") is in [API Authentication](../developers/api-authentication.md). Authentication flows RomM supports: - **Username + password** (default): local account, bcrypt-hashed, stored in the DB -- **OIDC**: single sign-on via an external IdP. See [OIDC Setup](oidc/index.md). +- **OIDC**: single sign-on via an external IdP - **Client API Tokens**: long-lived per-user tokens for companion apps and scripts -- **Device pairing**: short codes for bootstrapping a token onto a handheld, covered in [Client API Tokens](../ecosystem/client-api-tokens.md) -- **Kiosk mode**: unauthenticated read-only access, handy for public demos and shared terminals +- **Device pairing**: short codes for bootstrapping a token onto a handheld +- **Kiosk mode**: unauthenticated read-only access, handy for public demos ## Session config @@ -22,7 +22,7 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: | Variable | Default | What it controls | | ------------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually**, because it invalidates every active session and every outstanding invite link. | -| `ROMM_AUTH_SECRET_KEY_FILE` | _unset_ | Alternative: read the secret from a file. Useful with Docker secrets. | +| `ROMM_AUTH_SECRET_KEY_FILE` | | Alternative: read the secret from a file. Useful with Docker secrets. | | `SESSION_MAX_AGE_SECONDS` | 86400 (24 h) | How long a session cookie lives before the user has to sign in again. | | `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | 900 (15 min) | Short-lived OAuth2 access token TTL. | | `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | 2592000 (30 d) | Refresh token TTL for OAuth2. | @@ -30,7 +30,7 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: ## Local (username + password) -The default: accounts are created via [invitations, registration, or the Setup Wizard](invitations-and-registration.md), and passwords are bcrypt-hashed. RomM does not log or store plaintext passwords at any point. +Local accounts are created via [invitations, registration, admin setup, or the Setup Wizard](invitations-and-registration.md), and passwords are bcrypt-hashed. **Disable local password login entirely** (force OIDC-only): @@ -49,7 +49,7 @@ Until email-based self-serve reset lands, admins set passwords manually: **Administration → Users → Edit → New password → Save.** -The next login on that account will use the new password but existing sessions for that user remain valid until they expire. If that's a concern, revoke them explicitly by deleting all of the user's Client API Tokens. +The next login on that account will use the new password but existing sessions for that user remain valid until they expire. ## OIDC @@ -65,7 +65,7 @@ environment: - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid ``` -When OIDC is configured, the login page grows an "OIDC" button. Set `OIDC_AUTOLOGIN=true` to redirect straight to the IdP without the user having to click it. +When OIDC is configured, the login page shows an "OIDC" button. Set `OIDC_AUTOLOGIN=true` to redirect straight to the IdP without the user having to click it. ## Client API Tokens @@ -76,13 +76,13 @@ Create from **Administration → Client API Tokens**. Each token: - Belongs to a specific user - Carries a **subset** of that user's scopes (you choose which at creation time) - Has an optional expiry (no expiry = never expires until manually revoked) -- Can be "paired" to a device via a short code (covered in [Client API Tokens](../ecosystem/client-api-tokens.md)) +- Can be "paired" to a device via a short code Each user gets up to 25 active tokens, revokable from the same page. The API side ("how do I send this thing in a request?") lives in [API Authentication](../developers/api-authentication.md). ## Kiosk mode -Turns every GET endpoint into unauthenticated read-only access: anyone reaching the instance can browse but nobody can write, scan, upload, or manage users. +Grants unauthenticated, read-only access to nearly every GET endpoint. Anyone reaching the instance can browse but only a logged-in admin can write, scan, upload, or manage users. ```yaml environment: @@ -92,10 +92,10 @@ environment: Appropriate for: - Shared-terminal demos -- Public-facing "display" instances (e.g. a wall-mounted browse-only catalogue) +- Public-facing "display" instances - `demo.romm.app` -Authenticated users (when you do sign in) still see their full role. Kiosk only affects anonymous traffic. +Authenticated users (when you do sign in) will still see the full UI with all actions available to their role. ## Download-endpoint auth bypass @@ -104,7 +104,7 @@ environment: - DISABLE_DOWNLOAD_ENDPOINT_AUTH=true ``` -Skips auth on `GET /api/roms/{id}/content/…` and the firmware download endpoint. Exists so third-party apps that can't carry a bearer header (dumb emulators loading a ROM by URL) can still pull files. +Skips auth on `GET /api/roms/{id}/content/…` and the firmware download endpoint. Exists so third-party apps that can't carry a bearer header (like dumb emulators loading a ROM by URL) can still pull files. **Only enable this when the public internet can't reach RomM directly**, i.e. there's auth or an IP allowlist at the reverse-proxy layer. Otherwise you've just made your library world-downloadable. @@ -112,8 +112,6 @@ Skips auth on `GET /api/roms/{id}/content/…` and the firmware download endpoin To fully cut a user off: -1. **Administration → Users → Edit** → set password to something random and toggle to Viewer (so even if they're mid-session they can't do damage). -2. Delete all their Client API Tokens (**Administration → Client API Tokens**, filter by user). -3. Delete the user. - -Steps 1 + 2 together ensure any in-flight session and any token-based companion app lose access immediately, and step 3 removes the account. +1. **Administration → Client API Tokens** → Delete active tokens for that user. +2. **Administration → Users** → Disable the user. +3. **Administration → Users → Edit** → Delete the user. diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index 89b65b63..c211568b 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -40,7 +40,7 @@ From the RomM UI: **Profile → Client API Tokens → + New Token**. The token is shown **exactly once**. Copy it now. If you lose it, revoke and regenerate, because you can't get it back. -## Device pairing (short-code flow) +## Device pairing Typing a 44-character token into a handheld thumbstick isn't realistic. Instead: diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md index e309ddae..7a11c931 100644 --- a/docs/ecosystem/device-sync-protocol.md +++ b/docs/ecosystem/device-sync-protocol.md @@ -34,7 +34,7 @@ Required scopes: ## Registering a device -After [pairing](client-api-tokens.md#device-pairing-short-code-flow), the device registers itself: +After [pairing](client-api-tokens.md#device-pairing), the device registers itself: ```http POST /api/devices From d71842545ab8eff470fc437285a12b631675b3a9 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 19 Apr 2026 23:23:43 -0400 Subject: [PATCH 044/121] finsih firmware management --- docs/administration/firmware-management.md | 72 +++++----------------- 1 file changed, 14 insertions(+), 58 deletions(-) diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index 8b4ba3b7..45b878d8 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -5,35 +5,24 @@ description: Upload, associate, and serve BIOS/firmware files for emulation. # Firmware Management -Many emulated platforms require BIOS or firmware to boot: PlayStation (SCPH1001), Saturn, Game Boy Advance, Sega CD, and more. RomM tracks firmware files **per platform**, stores them on disk, and serves them to in-browser players (EmulatorJS / Ruffle) and companion apps that request them. +Many emulated platforms require BIOS or firmware to boot, or to reach a certain level of stability. RomM tracks firmware files **per platform**, stores them on disk, and serves them to in-browser players (EmulatorJS / Ruffle) and companion apps that request them. Firmware is **not** ROM. Keep the two separate: -- `/romm/library/roms/`: games you own dumps of -- `/romm/library/bios/` (Structure A) or `/romm/library/{platform}/bios/` (Structure B): system firmware +- `/romm/library/roms/`: game ROMs, homebrew, demos, etc. +- `/romm/library/bios/`: system firmware -Legality varies by jurisdiction. RomM does not ship firmware and the project cannot help you obtain it. -## Two paths to ingest firmware - -### Path 1: drop it in the folder, let the scan pick it up - -The usual workflow if you have firmware on disk already. - -1. Put the file in the right `bios/` folder (Structure A or B, see [Folder Structure](../getting-started/folder-structure.md)). -2. Run a scan. Firmware is picked up alongside ROMs. -3. It shows up in **Administration → Library Management → Firmware**. - -### Path 2: upload through the UI - -When you don't have shell access or you're uploading from a different machine. + +!!! important "Legality varies by jurisdiction" +RomM does not ship games or firmware, and the team cannot help you obtain BIOS files. Always check your local laws and the emulator's documentation for guidance on what you can legally use. -1. Go to the platform detail page (click a platform card from the home dashboard). -2. Click the **Upload** menu → **Upload Firmware**. -3. Drag and drop the file, or click to browse. -4. RomM puts it in the correct `bios/{platform}/` directory on disk. +## Ingesting firmware -Editors and Admins can upload firmware but Viewers can't (`firmware.write` scope required, see [Users & Roles](users-and-roles.md)). +1. Put the file in the right `bios/` folder (see [Folder Structure](../getting-started/folder-structure.md)) +2. Run a scan → Firmware is picked up alongside ROMs +3. Navigate to the platform's gallery and click the `CPU` icon in the top left +4. Firmware files will display at the bottom of the page ## Platform-specific firmware @@ -49,45 +38,12 @@ Common examples: | Saturn | `saturn_bios.bin`, `mpr-17933.bin` | `bios/saturn/` | | Nintendo DS | `firmware.bin`, `bios9.bin`, `bios7.bin` | `bios/nds/` | -File naming matters: emulators look for specific filenames. Double-check against the emulator core's documentation if something won't boot. - -## Listing and deleting firmware - -**Administration → Library Management → Firmware** shows every firmware file RomM knows about, grouped by platform. From here you can: - -- See file sizes and detected platform. -- Download a firmware file (useful for verifying integrity off-host). -- Delete a firmware entry. - - -!!! warning "Deleting firmware" - Deleting from the UI **does** remove the file from disk. If you want to keep the file but unlink it from RomM, move it out of the `bios/` folder first, then rescan. - -## API - -Firmware has a standard REST surface under `/api/firmware/`: - -- `GET /api/firmware`: list (optional `?platform_id=` filter) -- `POST /api/firmware`: upload -- `GET /api/firmware/{id}/content/{filename}`: download -- `POST /api/firmware/delete`: bulk delete - -Requires `firmware.read` / `firmware.write` scopes. See the [API Reference](../developers/api-reference.md). - -## Integration with in-browser play - -When a user launches a platform that requires firmware in EmulatorJS: - -1. The player checks RomM for a matching firmware file. -2. If present, it's served directly. No user action required. -3. If missing, the player surfaces an error ("firmware required") and the admin needs to upload one. - -Configure which emulator settings to expose to users via `emulatorjs.settings` in [`config.yml`](../reference/configuration-file.md). Details on the player side in [In-Browser Play](../using/in-browser-play.md). +File naming matters as emulators look for specific filenames. Double-check against the emulator core's documentation if something won't boot. ## Integration with companion apps -Handheld syncers (Grout, Argosy, DeckRommSync) can pull firmware alongside ROMs. They use the same Client API Token auth as for ROMs, so scope the token to include `firmware.read`. See [Client API Tokens](../ecosystem/client-api-tokens.md). +Handheld syncers (Grout, Argosy, DeckRommSync) can pull firmware alongside ROMs. They use the same Client API Token auth as for ROMs, so scope the token to include `firmware.read`. ## Backups -Firmware is user-owned data: back it up. `/romm/library/bios/` is part of your library mount, so if you're backing up the library volume, firmware is already covered. See [Backup & Restore](../install/backup-and-restore.md). +Firmware files are user-owned data, so back them up! If you're backing up the `/romm/library` volume, firmware is already covered (see [Backup & Restore](../install/backup-and-restore.md)). From 3482a34528ba1e12351d08829a344e64e03b8563 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Mon, 20 Apr 2026 22:28:33 -0400 Subject: [PATCH 045/121] more pages --- docs/administration/authentication.md | 2 +- docs/administration/firmware-management.md | 1 - docs/administration/index.md | 31 +--------- .../invitations-and-registration.md | 57 +++++-------------- 4 files changed, 19 insertions(+), 72 deletions(-) diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index 098fea8b..6b78edfc 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -22,7 +22,7 @@ Sessions are cookie-based and stored in Redis. Relevant env vars: | Variable | Default | What it controls | | ------------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ROMM_AUTH_SECRET_KEY` | _(required)_ | HS256 signing key for session tokens and JWTs. Generate with `openssl rand -hex 32`. **Never rotate this casually**, because it invalidates every active session and every outstanding invite link. | -| `ROMM_AUTH_SECRET_KEY_FILE` | | Alternative: read the secret from a file. Useful with Docker secrets. | +| `ROMM_AUTH_SECRET_KEY_FILE` | | Alternative: read the secret from a file. Useful with Docker secrets. | | `SESSION_MAX_AGE_SECONDS` | 86400 (24 h) | How long a session cookie lives before the user has to sign in again. | | `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` | 900 (15 min) | Short-lived OAuth2 access token TTL. | | `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` | 2592000 (30 d) | Refresh token TTL for OAuth2. | diff --git a/docs/administration/firmware-management.md b/docs/administration/firmware-management.md index 45b878d8..1f418dfe 100644 --- a/docs/administration/firmware-management.md +++ b/docs/administration/firmware-management.md @@ -12,7 +12,6 @@ Firmware is **not** ROM. Keep the two separate: - `/romm/library/roms/`: game ROMs, homebrew, demos, etc. - `/romm/library/bios/`: system firmware - !!! important "Legality varies by jurisdiction" RomM does not ship games or firmware, and the team cannot help you obtain BIOS files. Always check your local laws and the emulator's documentation for guidance on what you can legally use. diff --git a/docs/administration/index.md b/docs/administration/index.md index ebc49a85..0b997931 100644 --- a/docs/administration/index.md +++ b/docs/administration/index.md @@ -1,6 +1,6 @@ --- title: Administration -description: Running RomM for yourself and others: users, auth, metadata, scans, tasks, backups. +description: Running RomM for yourself and others. --- # Administration @@ -13,14 +13,14 @@ The end-user equivalent (how to actually play the games, build collections, uplo ### Users & access -- **[Users & Roles](users-and-roles.md)**: roles, the 19-scope model, how permissions add up +- **[Users & Roles](users-and-roles.md)**: roles, the scope model, how permissions add up - **[Invitations & Registration](invitations-and-registration.md)**: invite links, public signup, first-user setup - **[Authentication](authentication.md)**: session config, password reset, Client API Tokens for devices - **[OIDC Setup](oidc/index.md)**: Authelia, Authentik, Keycloak, PocketID, Zitadel, SSO + role mapping ### Content & library -- **[Metadata Providers](metadata-providers.md)**: all 13 providers, credentials, priority ordering +- **[Metadata Providers](metadata-providers.md)**: all providers, credentials, priority ordering - **[Scanning & Watcher](scanning-and-watcher.md)**: how scans work, scan modes, filesystem watcher - **[Firmware Management](firmware-management.md)**: BIOS/firmware uploads for emulation @@ -40,28 +40,3 @@ The end-user equivalent (how to actually play the games, build collections, uplo ### Keeping data safe - **[Backup & Restore](../install/backup-and-restore.md)**: routine backups, restore drill, host migration - -## The role model in thirty seconds - -Three built-in roles, all backed by the same scope system: - -| Role | Summary | -| ---------- | ------------------------------------------------------------------------------------------- | -| **Admin** | Full control. User management, task execution, every scope. First user always gets this. | -| **Editor** | Curate the library: edit ROMs, platforms, collections, upload firmware. No user management. | -| **Viewer** | Play games, manage own saves/states/profile. Read-only everywhere else. | - -Full scope matrix in [Users & Roles](users-and-roles.md). - -## The operator checklist - -First time running RomM for someone else? Do these, in this order: - -1. **Set `ROMM_AUTH_SECRET_KEY`** (via `openssl rand -hex 32`) and keep it forever. Changing it invalidates every session and invite link. -2. **Put it behind HTTPS.** See [Reverse Proxy](../install/reverse-proxy.md). Set `ROMM_BASE_URL` to the public URL. -3. **Set up at least one metadata provider** before the first scan. See [Metadata Providers](metadata-providers.md). -4. **Run the first scan** from the Scan page. See [Scanning & Watcher](scanning-and-watcher.md). -5. **Decide on invitations vs open registration.** See [Invitations & Registration](invitations-and-registration.md). -6. **Wire up OIDC** if you run an IdP. See [OIDC Setup](oidc/index.md). -7. **Configure backups.** See [Backup & Restore](../install/backup-and-restore.md). Test the restore before you need it. -8. **Pin the image tag** (`rommapp/romm:5.0.0`, not `:latest`) and read release notes before each upgrade. diff --git a/docs/administration/invitations-and-registration.md b/docs/administration/invitations-and-registration.md index 92f7c60e..19414993 100644 --- a/docs/administration/invitations-and-registration.md +++ b/docs/administration/invitations-and-registration.md @@ -1,18 +1,17 @@ --- title: Invitations & Registration -description: Inviting users, public signup, the first-user flow, and role assignment at sign-up. +description: Inviting users, the first-user flow, and role assignment. --- # Invitations & Registration -Three ways a new account ends up on a RomM instance: +There are three ways a new account ends up on a RomM instance: -1. **First-user setup**: the person who completes the Setup Wizard. +1. **First-admin setup**: the person who completes the Setup Wizard. 2. **Invite link**: an admin generates a one-shot link carrying a pre-assigned role. -3. **Public registration**: anyone who reaches `/register` signs themselves up as a Viewer. Off by default. -4. **OIDC auto-provisioning**: first login through your IdP creates a matching account. Covered in [OIDC Setup](oidc/index.md). +3. **OIDC auto-provisioning**: first login through your IdP creates a matching account (covered in [OIDC Setup](oidc/index.md)). -## First-user setup +## First-admin setup When a fresh RomM container starts against an empty database, hitting any page redirects to the **Setup Wizard**. The wizard collects a username, email, and password. The resulting account is **always an Admin**, regardless of any env var. @@ -23,15 +22,16 @@ environment: - DISABLE_SETUP_WIZARD=true ``` -You'll then need to create the first admin via the API or by injecting a row at deploy time, because the UI won't offer a setup flow. +You'll then need to create the first admin via the API or by injecting a database row at deploy time, because the UI won't offer a setup flow. ## Invite links The recommended way to add users, because it avoids you ever touching their password. -1. **Administration → Users → Invite.** Pick a role (Viewer, Editor, Admin). -2. RomM generates a single-use URL. Copy it and send it to the invitee. -3. When they open it, they pick their own username and password. RomM creates the account with the role you chose and logs them straight in. +1. **Administration → Users c Invite.** Pick a role (Viewer, Editor, Admin). +2. RomM generates a single-use URL → copy it and send it to the invitee. +3. When they open it, they pick their own username and password. +4. RomM creates the account with the role you chose and logs them straight in. Invite tokens are **single-use** and **time-limited**. Defaults: @@ -41,43 +41,16 @@ Invite tokens are **single-use** and **time-limited**. Defaults: Expired links return a clear error on the `/register` page. Generate a new one from the Users panel. - -!!! tip "Invitations over HTTPS" - Invite URLs include a signed token, so they're not useful to anyone without RomM's `ROMM_AUTH_SECRET_KEY`. Still, send them over a trusted channel, because once someone has a valid invite URL, they can claim the account. - -## Public self-registration - -Off by default. To let anyone with the `/register` URL create a Viewer account with no invite: - -```yaml -environment: - - ALLOW_PUBLIC_REGISTRATION=true -``` - -When on, the login page grows a "Register" link and `/register` becomes an open endpoint. - -Appropriate for: - -- Instances behind auth at the reverse-proxy layer (Authelia, Cloudflare Access, an IP allowlist). RomM's registration is just paperwork once the proxy has already authenticated the visitor. -- Truly public or group-shared instances where you genuinely want open signup - -Inappropriate for everything else. **If RomM is exposed to the internet with no upstream auth, leave this off**: it's the single fastest way to fill your DB with spam accounts. - -Anyone who signs up this way is a Viewer. Promote them manually from **Administration → Users → Edit** if needed. - ## Role assignment at sign-up -| Sign-up method | Role assigned | -| ------------------------------------------------------ | ------------------------------------------------------------ | -| First-user Setup Wizard | Admin (always) | -| Invite link | Whatever role the admin picked when generating the link | -| Public registration (`ALLOW_PUBLIC_REGISTRATION=true`) | Viewer | -| OIDC first login | Default Viewer, or mapped from claims via `OIDC_CLAIM_ROLES` | +| Sign-up method | Role assigned | +| ----------------------- | ------------------------------------------------------------ | +| First-user Setup Wizard | Admin (always) | +| Invite link | Whatever role the admin picked when generating the link | +| OIDC first login | Default Viewer, or mapped from claims via `OIDC_CLAIM_ROLES` | Changing a user's role afterwards is a normal admin action. See [Users & Roles](users-and-roles.md). ## Password reset Admins can reset passwords manually in **Administration → Users → Edit → New password**. A temporary password will be printed to the container's logs. - -See [Authentication](authentication.md) for the session and token side of the auth model. From 59c09c391cd7fb53fdaf45f24ad8c1ad1df49a15 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Thu, 23 Apr 2026 12:08:22 -0400 Subject: [PATCH 046/121] fix all trunk issues --- .trunk/configs/.markdownlint.yaml | 5 + .trunk/trunk.yaml | 6 + docs/about/faqs.md | 4 +- docs/administration/metadata-providers.md | 302 ++++++++++++++-------- docs/developers/api-authentication.md | 2 +- docs/developers/architecture.md | 2 +- docs/developers/development-setup.md | 16 +- docs/developers/websockets.md | 6 +- docs/ecosystem/client-api-tokens.md | 2 +- docs/ecosystem/muos-app.md | 4 +- docs/getting-started/quick-start.md | 11 +- docs/install/synology.md | 6 +- docs/install/truenas.md | 19 +- docs/install/unraid.md | 4 +- docs/platforms/custom-platforms.md | 2 + docs/reference/exports.md | 12 +- docs/using/in-browser-play.md | 4 +- docs/using/library.md | 4 +- 18 files changed, 256 insertions(+), 155 deletions(-) diff --git a/.trunk/configs/.markdownlint.yaml b/.trunk/configs/.markdownlint.yaml index b40ee9d7..407b688f 100644 --- a/.trunk/configs/.markdownlint.yaml +++ b/.trunk/configs/.markdownlint.yaml @@ -1,2 +1,7 @@ # Prettier friendly markdownlint config (all formatting rules disabled) extends: markdownlint/style/prettier + +# MkDocs pages set the page title via frontmatter and repeat it as a body H1. +# Stop MD025 from counting the frontmatter title as a heading. +MD025: + front_matter_title: "" diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 11d46ccf..6b048475 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -27,6 +27,12 @@ lint: - taplo@0.9.3 - trufflehog@3.88.12 - yamllint@1.35.1 + ignore: + # Snippet partials are included into other pages via pymdownx.snippets, + # so they intentionally lack a top-level heading and standalone structure. + - linters: [markdownlint] + paths: + - docs/resources/snippets/** # actions: # enabled: # - trunk-announce diff --git a/docs/about/faqs.md b/docs/about/faqs.md index ab5af111..4c2cd2b8 100644 --- a/docs/about/faqs.md +++ b/docs/about/faqs.md @@ -79,7 +79,7 @@ Most common reasons: Full troubleshooting steps can be found in [Scanning Troubleshooting](../troubleshooting/scanning.md). -## My scan finds platforms but no games inside them. +## My scan finds platforms but no games inside them This is almost always a mount-depth issue. RomM expects the _parent_ of your `roms/` folder mounted to `/romm/library`, not the `roms/` folder itself. If your files live at `/opt/romm/library/roms/gbc/game.gbc`, mount `/opt/romm/library` to `/romm/library`, then re-scan. @@ -162,7 +162,7 @@ We don't give ETAs on individual features. Open (or upvote) an issue at [rommapp Dropped in 3.0 for stability reasons; use MariaDB, MySQL, or Postgres instead. See [Databases](../install/databases.md). -## I found a bug, or I need help. +## I found a bug, or I need help For bugs, open an issue at [rommapp/romm](https://github.com/rommapp/romm/issues). For questions, ask in `#romm-support` on [Discord](https://discord.gg/romm). Either way, include: diff --git a/docs/administration/metadata-providers.md b/docs/administration/metadata-providers.md index e8967e79..9298aed6 100644 --- a/docs/administration/metadata-providers.md +++ b/docs/administration/metadata-providers.md @@ -1,185 +1,279 @@ ---- -title: Metadata Providers -description: Configure the thirteen metadata sources RomM supports: IGDB, ScreenScraper, MobyGames, RetroAchievements, SteamGridDB, Hasheous, PlayMatch, LaunchBox, TheGamesDB, Flashpoint, HowLongToBeat, gamelist.xml, Libretro. ---- + + -# Metadata Providers - -RomM pulls game metadata (titles, descriptions, cover art, screenshots, manuals, achievement data, completion times) from up to **thirteen** providers. You don't need all of them. This page covers the recommended combinations and per-provider setup. - -Configure providers either via env vars (below) or interactively in **Administration → Metadata Sources** in the UI. Scan priority (which provider wins when two disagree) is set in [`config.yml`](../reference/configuration-file.md). See `scan.priority.metadata` and `scan.priority.artwork`. +RomM supports multiple metadata providers to enrich your game library with titles, descriptions, cover art, and achievements. You don't need all providers, so this guide covers [popular combos](#popular-combos) and [setup instructions](#setup-instructions). ## Popular combos -### ⭐ The Chef's Choice: Hasheous + IGDB + SteamGridDB + RetroAchievements +Here are some combinations you can use based on your needs: -- Covers 135+ popular systems -- **Hasheous** does hash-based matching and proxies IGDB data (titles, descriptions, artwork). -- **IGDB** adds related games, screenshots, and broader metadata. -- **SteamGridDB** provides high-quality alternative cover art (opt-in per game via the "search cover" button). -- **RetroAchievements** overlays achievement progress. -- **Recommended default for most users.** +#### ⭐ The French Connection: [ScreenScraper](#screenscraper) + [Retroachievements](#retroachievements) -![Hasheous + IGDB + SteamGridDB + RetroAchievements](../resources/metadata_providers/2dcovers.png) +- Supports 125+ popular systems +- ScreenScraper provides titles, descriptions, cover art, screenshots and manuals + - Also supports hash-based matching (as of `v4.4`) + - With the option for 3D boxes and CD/cartridge covers +- Retroachievements provides achievement progress +- **Use this if you want to avoid Twitch/Amazon products** -### ⭐ The French Connection: ScreenScraper + RetroAchievements +![ScreenScraper + Retroachievements](../resources/metadata_providers/3dboxes.png) -- Covers 125+ popular systems -- **ScreenScraper** provides titles, descriptions, cover art (2D + optional 3D + CD), screenshots, manuals. Also supports hash-based matching since RomM 4.4. -- **RetroAchievements** overlays achievement progress. -- **Pick this if you want to avoid anything Twitch/Amazon-owned.** +#### ⭐ The Chef's Choice: [Hasheous](#hasheous) + [IGDB](#igdb) + [SteamGridDB](#steamgriddb) + [Retroachievements](#retroachievements) -![ScreenScraper + RetroAchievements](../resources/metadata_providers/3dboxes.png) +- Supports 135+. popular systems +- Hasheous provides hash-based matching and proxies IGDB data (titles, descriptions and artwork) +- IGDB adds additional metadata like related games and screenshots +- SteamGridDB provides high-quality alternative cover art +- Retroachievements provides achievement progress +- **This is the recommended setup for most users** -### The Twitch Fanboy: IGDB + PlayMatch +![Hasheous + IGDB + SteamGridDB + Retroachievements](../resources/metadata_providers/2dcovers.png) -- Covers the 200+ systems IGDB knows about -- IGDB-only metadata with PlayMatch's community-hosted hash-matching service bolted on for unmatched files -- **Use if you specifically want a single-provider solution backed by IGDB.** +#### The Twitch Fanboy: [IGDB](#igdb) + [PlayMatch](#playmatch) -### The Quick Starter: Hasheous only +- Supports the 200+ systems available on IGDB +- Provides titles, descriptions, cover art and related games from IGDB +- PlayMatch adds hash-based matching for unmatched files +- **Use this if you want a single-provider solution** -- Hash-based matching, fast scans, no API keys required -- Proxies titles/descriptions/artwork from IGDB -- **For users who want to avoid the IGDB/Twitch registration dance.** +#### The Quick Starter: [Hasheous](#hasheous) + +- Hash-based matching only ⚠️ +- Proxies titles, descriptions and cover art from IGDB +- Incredibly fast scan times +- **For users who want to avoid API keys** ## Setup instructions ### IGDB -[IGDB](https://www.igdb.com/) (Internet Game Database) is a popular metadata source with coverage for 200+ systems: titles, descriptions, screenshots, related games, and more. +[IGDB](https://www.igdb.com/) (Internet Game Database) is a popular metadata provider that offers metadata, cover art, screenshots, related games and more. + +To access the IGDB API you'll need a Twitch account and a valid phone number for 2FA verification. Up-to-date instructions are available in the [IGDB API documentation](https://api-docs.igdb.com/#account-creation). When registering your application in the Twitch Developer Portal, fill out the form like so: -Access requires a Twitch account and a phone number for 2FA. Up-to-date instructions live in the [IGDB API docs](https://api-docs.igdb.com/#account-creation). When registering your application in the Twitch Developer Portal: +- Name: Something **unique or random** like `romm-3fca6fd7f94dea4a05d029f654c0c44b` or `KVV8NDXMSRFJ2MRNPNRSL7GQT` +- OAuth Redirect URLs: `localhost` +- Category: `Application Integration` +- Client Type: `Confidential` -- **Name**: something unique, because picking an existing name fails silently. Use `romm-`. -- **OAuth Redirect URLs**: `localhost` -- **Category**: Application Integration -- **Client Type**: Confidential + +!!! important + The name you pick has to be unique! Picking an existing name will fail silently, with no error messages. We recommend using `romm-`, like `romm-3fca6fd7f94dea4a05d029f654c0c44b` -Set `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` from the values Twitch generates. +Note the client ID and secret that appear on screen, and use them to set `IGDB_CLIENT_ID` and `IGDB_CLIENT_SECRET` in your environment variables. -??? info "Screenshots" -![IGDB Creation](../resources/metadata_providers/1-igdb.png) -![IGDB Secret](../resources/metadata_providers/2-igdb.png) + +??? Screenshots + ![IGDB Creation](../resources/metadata_providers/1-igdb.png) + ![IGDB Secret](../resources/metadata_providers/2-igdb.png) ### ScreenScraper -[ScreenScraper.fr](https://screenscraper.fr/) is a French provider with wide coverage and good artwork (2D, 3D, CD/cartridge). +[ScreenScraper.fr](https://screenscraper.fr/) is a French provider that offers metadata, cover art, screenshots and manuals, along with the option for 3D boxes and CD/cartridge cover art. It supports a wide range of systems and is a great alternative to IGDB. -[Register](https://www.screenscraper.fr/membreinscription.php), then set `SCREENSCRAPER_USER` and `SCREENSCRAPER_PASSWORD`. +To access the ScreenScraper API, create a [ScreenScraper](https://www.screenscraper.fr/membreinscription.php) account and copy the **user** and **password** you just created to `SCREENSCRAPER_USER` and `SCREENSCRAPER_PASSWORD` respectively. ### MobyGames -Metadata, cover art, and screenshots. [Create an account](https://www.mobygames.com/user/register/), visit your profile, and follow the **API** link to request a key. Set `MOBYGAMES_API_KEY`. +MobyGames is a metadata provider that offers metadata, cover art and screenshots. + +To access the MobyGames API, [create a MobyGames account](https://www.mobygames.com/user/register/) and then visit your profile page. Click the **API** link under your user name to sign up for an API key. Copy the key shown and use it to set `MOBYGAMES_API_KEY`. -!!! important "MobyGames API is paid" - Access to the MobyGames API is a [paid, non-commercial-licensed feature](https://www.mobygames.com/info/api/#non-commercial). RomM will continue to support it but we recommend ScreenScraper as a free alternative. +!!! important + Access to the MobyGames API is a [paid feature](https://www.mobygames.com/info/api/#non-commercial). While we will continue to support it, we recommend using [ScreenScraper](#screenscraper) instead, as it is free to use. -### SteamGridDB +### LaunchBox -[SteamGridDB](https://www.steamgriddb.com/) serves custom cover art for games and collections. It's not used by the scanner directly. It surfaces in the **Search Cover** button when you manually edit a game's artwork. +The [LaunchBox](https://gamesdb.launchbox-app.com/) Games Database is a community-driven database that provides metadata, cover art, and screenshots. Like the Launchbox desktop application, RomM downloads the entire database locally and matches games based on their exact filenames. -Log in with a [Steam account](https://store.steampowered.com/join), go to your [API tab](https://www.steamgriddb.com/profile/preferences/api), and set `STEAMGRIDDB_API_KEY`. +To enable LaunchBox, set `LAUNCHBOX_API_ENABLED=true` and `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=true` in your environment variables. You can customize scheduled updates of the database by setting the frequency on the cron job with `SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON` (defaults to 5:00 AM every day). -### RetroAchievements +You must run a LaunchBox metadata update (either manually, or scheduled via cron) to generate a local `.xml` file with Launchbox metadata before using it as a metadata provider. The server will parse the local `.xml` file when trying to match a ROM and fetch metadata from this source. -[RetroAchievements](https://retroachievements.org/) provides achievement data and hash matching. Generate a web API key from your RA [settings page](https://retroachievements.org/settings) and set `RETROACHIEVEMENTS_API_KEY`. Run an **Unmatched** scan on the platforms you want matched. +### Hasheous -Each RomM user also links their own RA username in their profile to sync personal progression. A new **Achievements** tab appears on the **Personal** data panel once linked. +[Hasheous](https://hasheous.org/) is a free, open-source metadata provider that uses file hashes to match games. It proxies IGDB data for titles, descriptions, and cover art, and can provide Retroachievements IDs for matched games. -The RA database is cached locally. Refresh frequency is controlled by `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS` (default: 30). +Simply set `HASHEOUS_API_ENABLED=true` in your environment variables, and future scans will start using the [Hasheous API](https://hasheous.org/swagger/index.html). -??? info "Screenshots" -![RA API key](../resources/metadata_providers/1-ra.png) -![RA details](../resources/metadata_providers/2-ra.png) +### PlayMatch -### Hasheous +[PlayMatch](https://github.com/RetroRealm/playmatch) is a hash-based matching service used in conjunction with IGDB to provide better matching for games, hosted by a member of our community. -[Hasheous](https://hasheous.org/) is free and open-source, does hash-based matching, and proxies IGDB data (no IGDB creds required on your side). Flag with `HASHEOUS_API_ENABLED=true`. +To enable PlayMatch, set `PLAYMATCH_API_ENABLED=true` in your environment variables. -### PlayMatch +### SteamGridDB -[PlayMatch](https://github.com/RetroRealm/playmatch) is a community-hosted hash-matching service. Pair it with IGDB for better matching on unmatched files. Flag with `PLAYMATCH_API_ENABLED=true`. +SteamGridDB provides custom cover art for games or collections. It's not accessed through the scanner but from the "search cover" button when manually editing a game. -### LaunchBox +To access the SteamGridDB API, you need to login to their [website](https://www.steamgriddb.com/) with a [Steam account](https://store.steampowered.com/join). Once logged in, go to your [API tab under the preferences page](https://www.steamgriddb.com/profile/preferences/api). Copy the key shown and use it to set `STEAMGRIDDB_API_KEY`. -The [LaunchBox Games Database](https://gamesdb.launchbox-app.com/) is a community-driven catalogue. RomM downloads the full database locally and matches on exact filenames, just like the LaunchBox desktop app. +### RetroAchievements -```yaml -environment: - - LAUNCHBOX_API_ENABLED=true - - ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=true - - SCHEDULED_UPDATE_LAUNCHBOX_METADATA_CRON=0 5 * * * # default: 5am daily -``` +RomM is able to display your achievements from [RetroAchievements](https://retroachievements.org/). To sync it with your RomM instance, you need to generate an API key from your RetroAchievements account in your [settings](https://retroachievements.org/settings). -Run at least one LaunchBox update (manually from the Scan page, or wait for the cron) before using it as a scan source, because RomM won't match against an empty local DB. +Copy the key shown and use it to set `RETROACHIEVEMENTS_API_KEY` and perform a `UNMATCHED` scan targeting the platform you want to match with RetroAchievements. -### TheGamesDB +After that, each user needs to set their own username in their profile and sync it with RetroAchievements. A new `Achievements` tab will appear in the `Personal` tab in the game details. -[TheGamesDB](https://thegamesdb.net/) is a free community database that doesn't require credentials. Flag with `TGDB_API_ENABLED=true`. +To avoid unnecessary API calls, a cached file with the RA database is stored in RomM. Refresh time for that cache file can be changed with the environment variable `REFRESH_RETROACHIEVEMENTS_CACHE_DAYS`. + + +??? Screenshots + ![RA API key](../resources/metadata_providers/1-ra.png) + ![RA details](../resources/metadata_providers/2-ra.png) ### Flashpoint -The [Flashpoint Project Database](https://flashpointproject.github.io/flashpoint-database/) covers 180,000+ Flash and browser-based games, the thing Ruffle is for. Flag with `FLASHPOINT_API_ENABLED=true`. Run an **Unmatched** scan to update existing platforms. +The [Flashpoint Project Database](https://flashpointproject.github.io/flashpoint-database/) is a project that enables metadata for 180,000+ flash and browser-based games. Enable this metadata source with the `FLASHPOINT_API_ENABLED=true` environment variable. If you are adding this provider to an existing RomM setup, perform a `UNMATCHED` scan with Flashpoint selected to update an existing platform. ### HowLongToBeat -[HowLongToBeat](https://howlongtobeat.com/) adds game completion times (Main, Main + Extras, Completionist) to supported games. Flag with `HLTB_API_ENABLED=true`. +The [HowLongToBeat](https://howlongtobeat.com/) project provides game completion times for more than 84,000 games. Enable this metadata source with the `HLTB_API_ENABLED=true` environment variable. If you are adding this provider to an existing RomM setup, perform a `UNMATCHED` scan with HowLongToBeat selected to update an existing platform. -A new **Time to Beat** tab appears on matched games' detail pages. +Game completion times will be added to a new tab on the details page for supported matched games. -### gamelist.xml (ES-DE / Batocera) +### ES-DE gamelist.xml -If you came from EmulationStation / ES-DE / Batocera, you already have `gamelist.xml` files with metadata and media. RomM can parse these as a metadata source. - -Expected layout: +EmulationStation, and it's modern successor ES-DE, use a custom XML format to store game metadata. RomM can parse this format and import the assets as cover art and screenshots. You'll need to store the gamelist.xml file and any related assets under the platform folder: + ```yaml library/ └─ roms/ -└─ gba/ -├─ game_1.gba -├─ game_2.gba -├─ gamelist.xml -├─ 3dboxes/ -├─ covers/ -├─ screenshots/ -└─ ... + └─ gba/ + ├─ game_1.gba + ├─ game_2.gba + ├─ gamelist.xml + ├─ 3dboxes/ + │ ├─ game_1.png + │ └─ game_2.png + ├─ covers/ + ├─ screenshots/ + └─ etc... ``` + +```xml + + + + + ./game.gba + + + Game Title + A fun game to play + en + USA + + + 0.8 + 19990615T000000 + Developer Inc. + Publisher Co. + Game Series + Action + 1-2 + f1234567890abcdef1234567890abcde + + + ./images/game.png + ./covers/game.png + ./backcovers/game.png + ./3dboxes/game.png + ./fanart/game.png + ./manuals/game.pdf + ./marquees/game.png + ./miximages/game.png + ./physicalmedia/game.png + ./screenshots/game.png + ./titlescreens/game.png + ./thumbnails/game.png + + + + ... + + ... + +``` + +#### ES-DE metadata + +Here are the text properties that will be read from `gamelist.xml`. + +| Property Name | Description | +| ------------- | -------------------------------- | +| name | Game title | +| desc | Game description or synopsis | +| lang | Game language (en, es, it, etc.) | +| region | Game region (us, eu, jp, etc.) | +| rating | Game rating score | +| releasedate | Game release date | +| developer | Developer company | +| publisher | Publisher company | +| family | Game franchise or series | +| genre | Game category or type | +| players | Number of players supported | +| md5 | ROM file hash identifier | + +#### ES-DE media + +RomM has two ways of mapping media files: first it looks at `gamelist.xml` for properties, and it falls back to looking at nested folders for images that have the same name as the ROM. + +| Property Name | Folder Name | Description | +| ------------- | ------------- | -------------------------------------- | +| image | images | General game image | +| cover | covers | Front cover artwork | +| backcover | backcovers | Back cover artwork | +| box3d | 3dboxes | 3D box artwork | +| fanart | fanart | Fan-made artwork | +| manual | manuals | Game instruction manual | +| marquee | marquees | Arcade game marquee or header | +| miximage | miximages | Composite or mixed artwork | +| physicalmedia | physicalmedia | Physical media (cartridge, disc, etc.) | +| screenshot | screenshots | In-game screenshot | +| title_screen | titlescreens | Game title screen | +| thumbnail | thumbnails | Small preview image | +| video | videos | Gameplay video or trailer | + #### ES-DE settings -Two edits in the ES-DE settings file so ES-DE writes its metadata and media into the RomM-expected location: +Here are the settings you need to change so RomM can read your artwork and gamelist.xml files from the same folder that holds your ROMs. + +1. Open the ES-DE settings file: + + - Linux / macOS: `~/ES-DE/settings/es_settings.xml` + - Windows: `C:\Program Files\ES-DE\settings\es_settings.xml` -- Linux/macOS: `~/ES-DE/settings/es_settings.xml` -- Windows: `C:\Program Files\ES-DE\settings\es_settings.xml` +2. Make these two edits (add the lines if they don’t exist): ```xml ``` -`MediaDirectory` puts artwork next to ROMs, and `LegacyGamelistFileLocation` writes `gamelist.xml` next to ROMs instead of in the ES-DE config folder. If you already have scraped assets, move the contents of `~/ES-DE/downloaded_media/` and `~/ES-DE/gamelists/` into the ROM folders. +- `MediaDirectory="/path/to/ROMs/folder"` download artwork into the same directory that contains the ROMs (should match `ROMDirectory`) +- `LegacyGamelistFileLocation="true"` forces gamelist.xml to be written next to the ROMs instead of inside the ES-DE config folder -### Libretro +3. If you already have scraped artwork, copy/move the systems from `~/ES-DE/downloaded_media/` and `~/ES-DE/gamelists/` into your ROMs folder -Libretro's retro core metadata is used internally for platform mapping and fallback artwork: no env flag, no credentials. Nothing to configure. RomM uses it automatically when it knows the libretro core for a platform. +After a restart, ES-DE will place new artwork and the updated gamelist.xml directly in `roms//`, which is the layout RomM expects. -## Metadata tags in filenames +## Metadata Tags in Filenames -RomM honours inline tags in ROM filenames to force a match against a specific provider ID: +Scans will now parse custom metadata tags in the filename that match specific patterns, and use them to fetch game metadata for the specified ID. The supported tags are: -| Tag | Provider | -| ------------------ | --------------------------------------------------- | -| `(igdb-xxxx)` | [IGDB](https://www.igdb.com/) | -| `(moby-xxxx)` | [MobyGames](https://www.mobygames.com/) | -| `(ra-xxxx)` | [RetroAchievements](https://retroachievements.org/) | -| `(ssfr-xxxx)` | [ScreenScraper](https://screenscraper.fr/) | -| `(launchbox-xxxx)` | [LaunchBox](https://gamesdb.launchbox-app.com/) | -| `(hltb-xxxx)` | [HowLongToBeat](https://howlongtobeat.com/) | +(igdb-xxxx) for [IGDB](https://www.igdb.com/) +(moby-xxxx) for [MobyGames](https://www.mobygames.com/) +(ra-xxxx) for [RetroAchievements](https://retroachievements.org/) +(ssfr-xxxx) for [ScreenScraper](https://screenscraper.fr/) +(launchbox-xxxx) for [Launchbox](https://gamesdb.launchbox-app.com/) +(hltb-xxxx) for [HowLongToBeat](https://howlongtobeat.com/) -RomM will **not** rename your files to add these. They're opt-in, and renaming would conflict with other tooling that walks the filesystem. +Filenames will not be renamed by RomM to add tags, as they are a non-standard formatting system and could create conflicts with other software. ## Priority and conflict resolution @@ -213,4 +307,4 @@ scan: Reorder these lists to taste. For example, put `ss` first if you prefer ScreenScraper boxart, or move `hltb` up if you care about completion times more than descriptions. -See the full [Configuration File reference](../reference/configuration-file.md) for everything `scan.priority` can do. +See the full [Configuration File reference](../reference/configuration-file.md) for everything `scan.priority` can do diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index 938c9448..e9f839de 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -81,7 +81,7 @@ grant_type=password&username=alice&password=s3cret&scope=roms.read%20roms.write Access tokens are HS256-signed JWTs valid for ~15 minutes. Send them as: -``` +```http Authorization: Bearer eyJhbGciOi... ``` diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index 323c997a..9375698b 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -35,7 +35,7 @@ Key sub-directories: A running RomM container hosts several cooperating processes: -``` +```ascii ┌─────────────────────────────────────────────────────────┐ │ docker container │ │ │ diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index f60a75cd..2e33ca00 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -13,7 +13,7 @@ If you're contributing, also read [Contributing](contributing.md). Simplest. One command brings up the whole stack. -### 1. Clone + mock the library +1. Clone + mock the library Get the code and create the minimal fixtures RomM expects at runtime: @@ -53,7 +53,7 @@ That's it. RomM is on `http://localhost:3000`. Source mounts are live: edit back Faster iteration cycles than Docker if you're touching Python a lot. -### 1. Clone + mock the library +1. Clone + mock the library Same as Docker Option 1. @@ -69,7 +69,7 @@ touch romm_mock/config/config.yml cp env.template .env ``` -### 2. System dependencies +2. System dependencies ```sh # Debian / Ubuntu @@ -78,7 +78,7 @@ sudo apt install libmariadb3 libmariadb-dev libpq-dev Adjust for your distro. The key libraries are the MariaDB connector and libpq for Postgres. -#### RAHasher (optional) +### RAHasher (optional) Only needed if you're working on RetroAchievements hash calculation. **Not supported on macOS, skip.** @@ -93,7 +93,7 @@ sudo cp ./bin64/RAHasher /usr/bin/RAHasher cd .. ``` -### 3. Python environment +3. Python environment RomM uses [uv](https://docs.astral.sh/uv/getting-started/installation/): @@ -105,7 +105,7 @@ source .venv/bin/activate uv sync --all-extras --dev ``` -### 4. Start supporting services +4. Start supporting services MariaDB and Valkey come up via the dev compose file: @@ -113,7 +113,7 @@ MariaDB and Valkey come up via the dev compose file: docker compose up -d ``` -### 5. Run the backend +5. Run the backend Alembic migrations run automatically on start. @@ -122,7 +122,7 @@ cd backend uv run python3 main.py ``` -### 6. Frontend +6. Frontend In a second terminal: diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md index 5ced6227..42e9deef 100644 --- a/docs/developers/websockets.md +++ b/docs/developers/websockets.md @@ -3,6 +3,8 @@ title: WebSockets description: RomM's two Socket.IO endpoints for live updates and Netplay coordination. --- + + # WebSockets RomM uses **Socket.IO** for real-time communication. Two endpoints: @@ -37,10 +39,6 @@ If auth fails, the connection is closed with an error event. ## `/ws/socket.io`: general events -### Namespaces - -Default namespace. No sub-namespacing in 5.0. - ### Server → client events | Event | Payload | When | diff --git a/docs/ecosystem/client-api-tokens.md b/docs/ecosystem/client-api-tokens.md index c211568b..8ba8533c 100644 --- a/docs/ecosystem/client-api-tokens.md +++ b/docs/ecosystem/client-api-tokens.md @@ -46,7 +46,7 @@ Typing a 44-character token into a handheld thumbstick isn't realistic. Instead: ### Flow -``` +```ascii ┌───────────┐ ┌───────────┐ │ Device │ │ RomM │ └─────┬─────┘ └─────┬─────┘ diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md index 09b0d4d8..945e8cad 100644 --- a/docs/ecosystem/muos-app.md +++ b/docs/ecosystem/muos-app.md @@ -46,9 +46,7 @@ Installation uses muOS's [Archive Manager](https://muos.dev/installation/archive !!! tip "Use a dedicated account or token" - The username/password lives in plaintext on the SD card. Use a dedicated Viewer-role account for the handheld, not your admin credentials. - - Once RomM's 5.0 Client API Token flow is wired into the muOS app (planned), prefer pairing via token instead of password. + The username/password lives in plaintext on the SD card. Use a dedicated Viewer-role account for the handheld, not your admin credentials. Once RomM's 5.0 Client API Token flow is wired into the muOS app (planned), prefer pairing via token instead of password. ## Installing: EmulationStation (via PortMaster) diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index 17549ad4..41a61a74 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -1,8 +1,10 @@ --- title: Quick Start -description: Get a RomM 5.0 instance running in about fifteen minutes using Docker Compose. +description: Get a RomM instance running in about fifteen minutes --- + + # Quick Start This guide gets a RomM instance up and running with the default stack (MariaDB + Valkey) using Docker Compose. If you're on Unraid, Synology, TrueNAS, or Kubernetes, start there instead: the [Install & Deploy](../install/index.md) section has platform-specific guides. @@ -23,10 +25,11 @@ You'll need: Start from the reference file shipped in the RomM repo. A known-good, minimally-edited version is included below. Save it as `docker-compose.yml` in an empty directory on your host. + ???+ example "docker-compose.yml" -`yaml - --8<-- "quick-start.docker-compose.yml" - ` + `yaml + --8<-- "quick-start.docker-compose.yml" + ` You'll want to edit the following values before launching: diff --git a/docs/install/synology.md b/docs/install/synology.md index 17f40adf..da2b5f2f 100644 --- a/docs/install/synology.md +++ b/docs/install/synology.md @@ -74,10 +74,12 @@ Recommended before the first scan. Full walkthrough in [Metadata Providers](../a The Synology-flavoured compose file: MariaDB on port `3309` externally (to avoid colliding with Synology's built-in MariaDB), UID/GID customisation, simplified healthcheck: + ???+ example "docker-compose.yml" -`yaml + + Replace placeholder UIDs, GIDs, passwords, API keys, and `ROMM_AUTH_SECRET_KEY` with your own before starting. diff --git a/docs/install/truenas.md b/docs/install/truenas.md index 090d35d2..441c01f6 100644 --- a/docs/install/truenas.md +++ b/docs/install/truenas.md @@ -15,13 +15,13 @@ This guide covers **TrueNAS SCALE**. TrueNAS CORE isn't supported because its Fr ## Option A: App Catalog (recommended) -### 1. Open the RomM app +1. Open the RomM app **Apps** (left nav) → **Discover Apps** → search `RomM` → **Install**. ![RomM app](../resources/truenas/appstore.png) -### 2. Fill in the install form +2. Fill in the install form Step through the installation UI. You'll be asked for the same set of env vars as [Quick Start](../getting-started/quick-start.md), and most defaults work. Things to look at: @@ -32,7 +32,7 @@ Step through the installation UI. You'll be asked for the same set of env vars a ![RomM Library Example](../resources/truenas/app-config.png) -### 3. Install +3. Install Save. TrueNAS provisions the container + DB + Valkey, runs migrations, and exposes the web UI on the port you configured. If it won't boot, jump to [Troubleshooting](#troubleshooting). @@ -40,22 +40,23 @@ Save. TrueNAS provisions the container + DB + Valkey, runs migrations, and expos Use this path when the App Catalog has a bug, or when you want more flexibility than the install UI exposes. -### 1. Open the YAML install +1. Open the YAML install **Apps** → **Discover Apps** → **Install via YAML**. ![Install via YAML](../resources/truenas/install-via-yaml.png) -### 2. Paste the compose file +2. Paste the compose file Fill in the empty values with credentials you created in [Quick Start](../getting-started/quick-start.md). + ???+ example "docker-compose.yml" -`yaml - --8<-- "truenas.docker-compose.yml" - ` + `yaml + --8<-- "truenas.docker-compose.yml" + ` -### 3. Install +3. Install Save. Same troubleshooting applies, see below. diff --git a/docs/install/unraid.md b/docs/install/unraid.md index 6788ddad..45d42242 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -20,7 +20,7 @@ Both end up with the same running stack. ## Community Apps template -### Prerequisites +### Prerequisites for CA - [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) installed - A custom Docker bridge network so RomM and MariaDB can talk to each other by container name. Skip this and you'll hit DNS issues that look like everything else. @@ -66,7 +66,7 @@ Apply, head back to the **Docker** tab, and you should see both containers runni ## Docker Compose Manager -### Prerequisites +### Prerequisites for DCM - [Community Apps plugin](https://forums.unraid.net/topic/38582-plug-in-community-applications/) - [Docker Compose Manager plugin](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/) from CA diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index 8cc0b67e..1a5d289c 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -3,6 +3,8 @@ title: Custom Platforms description: Add platforms RomM doesn't natively support, plus custom platform icons. --- + + # Custom Platforms RomM ships with support for ~400 platforms. If yours isn't in [the list](supported-platforms.md), you can still load it as a custom platform. RomM just won't have metadata-provider coverage for it. diff --git a/docs/reference/exports.md b/docs/reference/exports.md index c9120d34..0da5aaf2 100644 --- a/docs/reference/exports.md +++ b/docs/reference/exports.md @@ -11,7 +11,7 @@ RomM can emit your library metadata in formats other frontends expect. Useful wh ES-DE, Batocera, and compatibles look for a `gamelist.xml` in each platform folder. RomM can generate these automatically. -### Enable via `config.yml` +**Enable via `config.yml`** ```yaml scan: @@ -24,8 +24,6 @@ scan: With `export: true`, every scan writes a `gamelist.xml` into the platform folder, and downloads the selected media into sibling folders (`covers/`, `screenshots/`, etc.) that ES-DE expects. -### Format - Standard ES-DE / EmulationStation format: ```xml @@ -45,7 +43,7 @@ Standard ES-DE / EmulationStation format: ``` -### API +### API endpoint Trigger an export on demand: @@ -79,7 +77,7 @@ See also [Metadata Providers → gamelist.xml](../administration/metadata-provid [Pegasus](https://pegasus-frontend.org/) is an alternative gaming frontend with its own metadata format. RomM can emit a `metadata.pegasus.txt` per platform. -### Enable via `config.yml` +**Enable via `config.yml`** ```yaml scan: @@ -87,8 +85,6 @@ scan: export: true ``` -### Format - Human-readable text format: ```text @@ -107,7 +103,7 @@ assets.box: ./covers/Super Metroid.png assets.screenshot: ./screenshots/Super Metroid.png ``` -### API +### API request ```http POST /api/export/pegasus diff --git a/docs/using/in-browser-play.md b/docs/using/in-browser-play.md index 16fce1eb..4951e67a 100644 --- a/docs/using/in-browser-play.md +++ b/docs/using/in-browser-play.md @@ -130,9 +130,7 @@ See [Netplay](netplay.md). One-page deep dive on hosting/joining, ICE servers, a !!! important "Ruffle needs the right platform folder" Ruffle only plays games from platform folders named `flash` or `browser`. If your Flash games are elsewhere, either rename the folder or add a [platform binding](../reference/configuration-file.md#systemplatforms) in `config.yml`. -### Controls - -Flash games were typically designed for mouse + keyboard, and Ruffle passes input through as-is. No controller mapping, so gamepad-only users will struggle with most Flash titles. +No controller mapping, so gamepad-only users will struggle with most Flash titles. ### Saves diff --git a/docs/using/library.md b/docs/using/library.md index 2febfe6f..1259f6d1 100644 --- a/docs/using/library.md +++ b/docs/using/library.md @@ -1,10 +1,8 @@ --- title: Library -description: Browse your RomM library: dashboard, cards, filters, search, and the platform/game views. +description: Browse your RomM library --- - - # Library The library is the heart of RomM. This page covers the day-to-day UI: the dashboard, game and platform cards, filters, search, and the detail views. From b7c7d9dc6077a31ea5c44fa26bdcdaeeda892cc1 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Fri, 24 Apr 2026 09:22:31 -0400 Subject: [PATCH 047/121] little cleanup --- docs/administration/observability.md | 12 +++++------- docs/administration/ssh-sync.md | 12 ++++++------ docs/developers/development-setup.md | 2 +- docs/developers/releasing.md | 14 +++++++------- docs/ecosystem/igir.md | 4 ++-- docs/ecosystem/playnite-plugin.md | 4 ++-- docs/install/unraid.md | 10 +++++----- docs/platforms/custom-platforms.md | 8 ++++---- 8 files changed, 32 insertions(+), 34 deletions(-) diff --git a/docs/administration/observability.md b/docs/administration/observability.md index ebe6fa85..f0fb836e 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -1,21 +1,19 @@ --- title: Observability -description: Logs, Sentry error tracking, OpenTelemetry, and the /heartbeat endpoint. +description: Logs, error tracking and telemetry --- # Observability -Four ways to know what RomM is doing: +It's often handy to know waht RomM is doing under the hood, especially when debugging a scan or task. RomM's observability stack includes: - **Container logs**: always available, the first stop -- **`/api/heartbeat`**: health + config summary for uptime monitors +- **`/api/heartbeat`** endpoint: health + config summary for uptime monitors - **Sentry**: opt-in error tracking with stack traces - **OpenTelemetry**: opt-in distributed tracing + metrics ## Logs -### Log levels - ```yaml environment: - LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR @@ -23,7 +21,7 @@ environment: - NO_COLOR=1 # 1 to disable colour entirely ``` -`INFO` is the default and sane for production. Drop to `DEBUG` only while debugging a specific issue, because RomM is chatty on DEBUG. +`INFO` is the default and the sane choice for production. Drop to `DEBUG` only while debugging a specific issue, because RomM is chatty on `DEBUG`. ### Reading logs @@ -35,7 +33,7 @@ ERROR: [RomM][ra_handler][2026-04-18 11:48:55] Invalid RetroAchievements A WARNING: [RomM][config_manager][2026-04-18 12:01:12] config.yml not found, using defaults ``` -Useful greps: +Useful grep commands: ```sh docker logs romm 2>&1 | grep ERROR diff --git a/docs/administration/ssh-sync.md b/docs/administration/ssh-sync.md index b22ab556..01d75b92 100644 --- a/docs/administration/ssh-sync.md +++ b/docs/administration/ssh-sync.md @@ -23,7 +23,7 @@ If none of that applies, you don't need this page. HTTPS + Client API Tokens is ## Configuring the server -### 1. Provision an SSH key for RomM +1. Provision an SSH key for RomM Generate a dedicated key on the host that runs the RomM container: @@ -33,7 +33,7 @@ ssh-keygen -t ed25519 -f ~/romm-sync-key -N "" -C "romm-sync" This produces `~/romm-sync-key` (private) and `~/romm-sync-key.pub` (public). Keep the private key readable only by you: `chmod 600 ~/romm-sync-key`. -### 2. Mount the key into the RomM container +2. Mount the key into the RomM container ```yaml services: @@ -48,7 +48,7 @@ Keep it read-only. RomM doesn't need to modify the key. `SSH_PRIVATE_KEY_PATH` can point anywhere inside the container, conventionally `/romm/.ssh/id_rsa`. -### 3. Restart RomM +3. Restart RomM ```sh docker compose up -d @@ -60,11 +60,11 @@ The Push-Pull Device Sync task will now use that key for outbound SSH connection For each handheld / device you want to sync: -### 1. Authorise the RomM key +1. Authorise the RomM key Copy the **public** key (`~/romm-sync-key.pub` from step 1 above) to the device's `~/.ssh/authorized_keys`. Exactly how depends on the device: Grout on muOS has a helper, others expose a filesystem you can `ssh-copy-id` into. -### 2. Register the device in RomM +2. Register the device in RomM Device registration is done through a companion app (typically Grout itself) using the [Client API Token pairing flow](../ecosystem/client-api-tokens.md). Once registered, RomM knows: @@ -73,7 +73,7 @@ Device registration is done through a companion app (typically Grout itself) usi - SSH port (default `22`) - Target paths on the device for saves, states, and any other synced assets -### 3. Test the connection +3. Test the connection From the RomM container, confirm SSH works: diff --git a/docs/developers/development-setup.md b/docs/developers/development-setup.md index 2e33ca00..f6e4008b 100644 --- a/docs/developers/development-setup.md +++ b/docs/developers/development-setup.md @@ -163,7 +163,7 @@ Trunk runs as a pre-commit hook automatically after install. Alternative install ## Tests -### One-time: create the test DB + user +### Create the test DB + user (onet-time) ```sh docker exec -i romm-db-dev mariadb -uroot -p < backend/romm_test/setup.sql diff --git a/docs/developers/releasing.md b/docs/developers/releasing.md index 60ad312c..48afeb0a 100644 --- a/docs/developers/releasing.md +++ b/docs/developers/releasing.md @@ -27,26 +27,26 @@ Alembic migrations run on every startup, and migrations are backwards-compatible ## Pre-release checklist -### 1. Merge pending PRs +1. Merge pending PRs - All release-milestoned PRs merged into `master` - CI green on `master` - Linter green: `trunk check --all` - Tests green: `uv run pytest` -### 2. Update version numbers +2. Update version numbers - `pyproject.toml` → `version = "X.Y.Z"` - `frontend/package.json` → `"version": "X.Y.Z"` - Any hardcoded version strings (`backend/__init__.py`, etc.). `rg '__version__'` or `rg '5\.0\.0'` to find them -### 3. Update `env.template` if needed +3. Update `env.template` if needed If the release adds / renames / removes env vars, `env.template` is the canonical reference. Add/rename/remove lines with inline comments. Keep alphabetical order per section. The docs site auto-generates [Environment Variables](../reference/environment-variables.md) from `env.template` on every release. Skip this step and the docs drift. -### 4. Update changelog +4. Update changelog `CHANGELOG.md` at repo root (if present): add a new section for the new version: @@ -69,7 +69,7 @@ The docs site auto-generates [Environment Variables](../reference/environment-va Link to the PRs / issues. This feeds into the GitHub Release notes. -### 5. Tag and push +5. Tag and push ```sh git checkout master @@ -80,7 +80,7 @@ git push origin 5.0.0 The tag triggers the Docker build workflow: `rommapp/romm:5.0.0` (full) and `rommapp/romm:5.0.0-slim` are built and published to Docker Hub + GHCR. -### 6. Publish the GitHub Release +6. Publish the GitHub Release From the GitHub UI or `gh` CLI: @@ -93,7 +93,7 @@ gh release create 5.0.0 \ Release notes come from the `CHANGELOG.md` entry. For major releases, add a migration-guide link to the top of the notes. -### 7. Move the `latest` and major tags +7. Move the `latest` and major tags The Docker workflow handles `5.0.0` and `5.0.0-slim` automatically. For `:latest` and `:5`: diff --git a/docs/ecosystem/igir.md b/docs/ecosystem/igir.md index 96bb3355..96e3ce1b 100644 --- a/docs/ecosystem/igir.md +++ b/docs/ecosystem/igir.md @@ -30,13 +30,13 @@ Igir works on a copy of your ROMs (never in place) to let you iterate on its con └── igir-romm-cleanup.sh # the script below ``` -### 1. Make a working copy +1. Make a working copy ```sh cp -r roms/ roms-unverified/ ``` -### 2. Download DAT files +2. Download DAT files DAT files are hash-referenced catalogues Igir matches against. diff --git a/docs/ecosystem/playnite-plugin.md b/docs/ecosystem/playnite-plugin.md index d0db7b57..72473d6a 100644 --- a/docs/ecosystem/playnite-plugin.md +++ b/docs/ecosystem/playnite-plugin.md @@ -37,13 +37,13 @@ Four paths, pick whichever's easiest: ## Setup -### 1. Configure at least one emulator in Playnite +1. Configure at least one emulator in Playnite The plugin needs to know how to launch a game. **If no emulators are configured, setup can't finish.** Playnite: **Menu → Library → Configure Emulators → Add emulator**. Add whatever emulators cover your ROMs (Dolphin, RetroArch, Duckstation, etc.). Built-in presets are fine for most common emulators. -### 2. Configure the plugin +2. Configure the plugin Playnite: **Menu → Library → Configure Integrations → RomM**. diff --git a/docs/install/unraid.md b/docs/install/unraid.md index 45d42242..eb35aab7 100644 --- a/docs/install/unraid.md +++ b/docs/install/unraid.md @@ -32,7 +32,7 @@ Both end up with the same running stack. ![console output](https://github.com/user-attachments/assets/bac31998-1911-4085-b115-8dd93d519b8b) -### 1. Install MariaDB +1. Install MariaDB From **Apps** → search `mariadb`. Only the [official `mariadb`](https://hub.docker.com/_/mariadb) and [linuxserver/docker-mariadb](https://github.com/linuxserver/docker-mariadb/pkgs/container/mariadb) templates are supported. **Prefer the official one.** @@ -46,7 +46,7 @@ Fill in the env vars. Names and sensible defaults live in the [reference `docker !!! warning "Network type" MariaDB's network type **must** be set to `Custom: romm`. Otherwise RomM can't resolve its hostname. -### 2. Install RomM +2. Install RomM From **Apps**, search `romm`, install the app labelled **OFFICIAL** (maintained by the RomM team, always current). @@ -56,7 +56,7 @@ Fill in env vars, ports, and paths per the [reference compose](docker-compose.md ![RomM docker tab](https://github.com/user-attachments/assets/4c4210c2-ed00-4790-a945-65cbe33620b0) -### 3. Done +3. Done Apply, head back to the **Docker** tab, and you should see both containers running. Access RomM at the IP:port highlighted below. @@ -77,7 +77,7 @@ After installing, a **Compose** section appears under the Docker Containers list ![Docker Compose Section](../resources/unraid/unraid-start.png) -### 1. Add the stack +1. Add the stack **Add New Stack** → name it **RomM** → OK. @@ -97,7 +97,7 @@ Save after each edit. !!! warning "Folder structure" Make sure your library matches one of the [supported folder layouts](../getting-started/folder-structure.md) before scanning. Unraid users often forget this step. -### 2. Bring it up +2. Bring it up Click **Compose Up**. diff --git a/docs/platforms/custom-platforms.md b/docs/platforms/custom-platforms.md index 1a5d289c..24899fb9 100644 --- a/docs/platforms/custom-platforms.md +++ b/docs/platforms/custom-platforms.md @@ -47,7 +47,7 @@ If your platform isn't in [the built-in icons list](https://github.com/rommapp/r To load your own: -### 1. Mount the platform icons directory +1. Mount the platform icons directory Bind-mount a host directory onto the container's icon path: @@ -58,11 +58,11 @@ services: - /path/to/your/icons:/var/www/html/assets/platforms ``` -### 2. Seed it with the official icons +2. Seed it with the official icons RomM's built-in icons live at [`frontend/assets/platforms`](https://github.com/rommapp/romm/tree/master/frontend/assets/platforms). Download them all and drop into your mounted directory, otherwise built-in platforms lose their icons too (because your mount overrides the whole directory). -### 3. Add your custom `.ico` files +3. Add your custom `.ico` files The filename has to **match the IGDB platform slug**. Examples: @@ -74,7 +74,7 @@ The filename has to **match the IGDB platform slug**. Examples: Find the slug in the URL of the platform's IGDB page, e.g. [igdb.com/platforms/acpc](https://www.igdb.com/platforms/acpc) → slug is `acpc`. -### 4. Restart RomM +4. Restart RomM `docker compose up -d` picks up the new icon mount. From d99b1701cc35dcdbce0ce9a27d816a3582a24842 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Fri, 24 Apr 2026 17:57:26 -0400 Subject: [PATCH 048/121] finish observability --- docs/administration/observability.md | 47 +++++++--------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/docs/administration/observability.md b/docs/administration/observability.md index f0fb836e..608464c1 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -33,7 +33,7 @@ ERROR: [RomM][ra_handler][2026-04-18 11:48:55] Invalid RetroAchievements A WARNING: [RomM][config_manager][2026-04-18 12:01:12] config.yml not found, using defaults ``` -Useful grep commands: +Some useful grep commands: ```sh docker logs romm 2>&1 | grep ERROR @@ -45,23 +45,13 @@ Deployment-specific log commands are in [Miscellaneous Troubleshooting → Viewi ## `/api/heartbeat` -A single-request health + config endpoint. Safe to hit anonymously (though some fields only appear for authenticated callers). +A single-request endpoint to fetch health and config information. Works when not logged in, though some fields only appear for authenticated callers. ```http GET /api/heartbeat ``` -Returns: - -- RomM version -- Whether the Setup Wizard is still pending -- Which metadata providers are enabled -- Which platforms have data -- OIDC config (redacted credentials) -- Scheduled-task schedule summary -- Watcher status - -Wire this to your uptime monitor. A failure here is real: the process is down or the DB/Valkey is unreachable. +Wire this to your uptime monitor. A failure here means that the process is down or the DB/Valkey is unreachable. ```bash # Basic uptime check @@ -73,17 +63,14 @@ curl -fsS https://romm.example.com/api/heartbeat > /dev/null \ Per-metadata-provider health: ```http -GET /api/heartbeat/metadata/igdb -GET /api/heartbeat/metadata/ss -GET /api/heartbeat/metadata/ra -... +GET /api/heartbeat/metadata/[igdb/ss/ra/...] ``` Useful when a scan is matching poorly and you want to know whether a provider is down on their side or misconfigured on yours. ## Sentry -Opt-in error tracking: nothing is sent without a DSN. +Opt-in error tracking: ```yaml environment: @@ -98,11 +85,9 @@ What's sent: What's not sent: ROM filenames, user credentials, metadata provider API keys. RomM filters sensitive parameters before reporting. -Suitable for self-hosted Sentry or [sentry.io](https://sentry.io/). - ## OpenTelemetry -Opt-in distributed tracing and metrics. Useful if you run RomM alongside other services and want unified observability. +If you're already using OpenTelemetry with your other apps and want unified observability: ```yaml environment: @@ -123,7 +108,7 @@ Exporters: - OTLP gRPC (default, port `4317`) - OTLP HTTP (port `4318`): set `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`. -Send to an OpenTelemetry Collector, then fan out to Tempo / Jaeger / Honeycomb / Datadog / Grafana Cloud / whatever you run. +Send to an OpenTelemetry Collector, then fan out to Tempo/Jaeger/Honeycomb/Datadog/Grafana/whatever you run. ## Task status @@ -138,19 +123,11 @@ Returns an array of every scheduled / manual / watcher task with current status ## Anti-patterns -- **Don't parse unstructured log lines** for metrics. Use OTEL or `/api/tasks/status`. -- **Don't log at DEBUG in production.** The volume is real and scans will drown in it. -- **Don't scrape HTML pages for health checks.** `/api/heartbeat` is the contract. HTML changes between versions, the API endpoint is stable. +- **Don't parse unstructured log lines** for metrics (use OTEL instead) +- **Don't log at DEBUG in production** as the volume is real and scans will drown in it +- **Don't scrape HTML pages for health checks**; HTML changes between versions while the API endpoint is stable ## Minimum recommended stack -For a homelab instance: - -- Default `INFO` logs into the container logs → forwarded to Loki / Promtail / whatever you already run -- `/api/heartbeat` hit every 60 seconds from Uptime Kuma / Gatus - -For a serious deployment: - -- Above, plus Sentry DSN configured -- Plus OpenTelemetry to the collector you already have -- Alert on: heartbeat failure, task stuck > 1 h, `ERROR`-level log spikes +- Default `INFO` logs into the container logs → forwarded to Loki/Promtail/whatever you already run +- `/api/heartbeat` hit every 60 seconds from Uptime Kuma/Gatus From f035bc872b74d0d36cdbfe981456ea75fecdfa09 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Fri, 24 Apr 2026 19:13:28 -0400 Subject: [PATCH 049/121] finish scanning --- docs/administration/scanning-and-watcher.md | 32 ++++++++++----------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 21fd646d..52cf7209 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -1,15 +1,15 @@ --- title: Scanning & Watcher -description: How RomM scans your library, the six scan modes, and the filesystem watcher. +description: Scanning your library, scan modes, and the filesystem watche --- # Scanning & Watcher RomM keeps its catalogue in sync with your filesystem through three mechanisms: -1. **Manual scans** you trigger from the Scan page. -2. **Scheduled scans** (default: nightly) run by the task runner. -3. **The filesystem watcher**: live reaction to files landing in or leaving your library. +1. **Manual scans** you trigger from the Scan page +2. **Scheduled scans** (default: nightly) run by the task runner +3. **The filesystem watcher** reacting to files landing in or leaving your library All three share the same scan engine and the same set of **scan modes**. @@ -23,7 +23,7 @@ Every scan picks one mode. Modes differ in what they touch, so use the most-targ | **Quick** | Skips files that already exist in the DB, with no metadata refresh. | Default for scheduled runs and the watcher. | | **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | | **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured). | -| **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash (pre-4.4) or when you suspect file corruption. | +| **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash or when you suspect file corruption. | | **Complete** | Full rescan, recalculating hashes and re-fetching metadata for everything. | Rarely, since it takes a long time. | You can further scope a scan to specific **platforms** and specific **metadata providers**, useful when only one provider has changed (e.g. just enabled Hasheous → Unmatched scan, Hasheous selected, on all platforms). @@ -34,11 +34,11 @@ You can further scope a scan to specific **platforms** and specific **metadata p - Platform checkboxes (select which to scan, leave empty to scan all) - Metadata provider toggles (overrides the default priority for this one scan) -- Advanced options: skip hashing (helpful on low-power hosts), target a LaunchBox refresh +- skip hashing (helpful on low-power hosts) - A live log of everything the scanner is doing - Per-platform progress panels with matched / unmatched / missing counts -A running scan survives browser refreshes, and the log streams over Socket.IO. Multiple admins opening the page see the same scan state. +A running scan survives browser refreshes, and the log streams over a websocket. Multiple users opening the scan page can see the same scan state. ## Scheduled scans @@ -48,21 +48,19 @@ Configured via env vars (full table in the [Scheduled Tasks reference](../refere | -------------------- | ----------- | ------------------------------------------------------------------------------------------------- | | `SCAN_INTERVAL_CRON` | `0 0 * * *` | Cron expression for the scheduled library scan. Runs a **Quick** scan by default. | | `SCAN_TIMEOUT_HOURS` | `1` | Hard cap: scans that exceed this are killed and logged. | -| `SCAN_WORKERS` | _auto_ | Concurrent worker processes for scanning; leave as auto unless you're tuning. | -| `SEVEN_ZIP_TIMEOUT` | _unset_ | Per-archive timeout for `.7z` extraction during scan; raise if scanning huge compressed ROM sets. | +| `SCAN_WORKERS` | `1` | Concurrent worker processes for scanning; leave as auto unless you're tuning. | +| `SEVEN_ZIP_TIMEOUT` | `60` | Per-archive timeout for `.7z` extraction during scan; raise if scanning huge compressed ROM sets. | To disable scheduled scans entirely, either unset the cron or set it to something unreachable (`SCAN_INTERVAL_CRON=0 0 31 2 *`). ## Filesystem watcher -The watcher tails your library folder and schedules scans in response to file events (files added, moved, or deleted). It's off by default on some deployments, so enable with: +The watcher tails your library folder and schedules scans in response to file events (files added, moved, or deleted). It's off by default, so enable it with: ```yaml environment: - - WATCHER_ENABLED=true - - RESCAN_ON_FILESYSTEM_CHANGE=true + - ENABLE_RESCAN_ON_FILESYSTEM_CHANGE=true - RESCAN_ON_FILESYSTEM_CHANGE_DELAY=10 # seconds before acting on an event - - WATCH_EXTENSIONS_ONLY=false # true to ignore events on non-ROM extensions ``` Behaviour: @@ -77,7 +75,7 @@ Behaviour: ### When **not** to enable the watcher - **Slow / high-latency filesystems** (SMB mounts, rclone mounts, anything not local disk): the watcher reacts to every event, flaky mounts generate a lot of them, so use scheduled scans instead. -- **Libraries under active write load from other tools** (e.g. a ROM manager constantly tagging files): the watcher will re-scan on every change, at best noisy and at worst a scan loop. +- **Libraries under active write load from other tools** (e.g. IGIR constantly tagging files): the watcher will re-scan on every change, at best noisy and at worst a scan loop. ### Watcher vs scheduled scan @@ -89,7 +87,7 @@ Behaviour: | Catches renames | Yes | Yes | | Survives a container restart | Yes, re-arms on startup | Yes | -Run both: the watcher handles day-to-day additions, and the scheduled scan is a safety net. +You can run both, where the watcher handles day-to-day additions, and the scheduled scan is a safety net. ## What gets excluded @@ -123,11 +121,11 @@ scan: language: [en, fr] ``` -When a metadata provider returns multiple regional variants (Japanese cover, US cover, European cover…), RomM picks according to this order. Same for localised titles. +When a metadata provider returns multiple regional variants (Japanese cover, US cover, European cover…), we pick according to this order, and the same goes for localised titles. ## Metadata source priority -Who wins when two providers disagree is covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution), though the short version is: `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. +Who wins when two providers disagree is covered in [Metadata Providers](metadata-providers.md#priority-and-conflict-resolution), though the short version is `scan.priority.metadata` and `scan.priority.artwork` in `config.yml`. ## Troubleshooting From e5ae931b6d057f7007b547f2c4404b185cb2f506 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 25 Apr 2026 09:24:59 -0400 Subject: [PATCH 050/121] finish scheduled tasks --- docs/Navigation.md | 1 - docs/administration/scanning-and-watcher.md | 2 +- docs/administration/scheduled-tasks.md | 96 +++------------------ docs/developers/index.md | 1 - docs/reference/environment-variables.md | 2 +- docs/reference/scheduled-tasks.md | 53 ------------ mkdocs.yml | 2 +- 7 files changed, 16 insertions(+), 141 deletions(-) delete mode 100644 docs/reference/scheduled-tasks.md diff --git a/docs/Navigation.md b/docs/Navigation.md index 4af7480e..d68c75eb 100644 --- a/docs/Navigation.md +++ b/docs/Navigation.md @@ -101,7 +101,6 @@ search: - Reference - [Environment Variables](reference/environment-variables.md) - [Configuration File](reference/configuration-file.md) - - [Scheduled Tasks](reference/scheduled-tasks.md) - [Exports](reference/exports.md) - [Feeds](reference/feeds.md) - [Ports & Endpoints](reference/ports-and-endpoints.md) diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 52cf7209..08b71032 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -42,7 +42,7 @@ A running scan survives browser refreshes, and the log streams over a websocket. ## Scheduled scans -Configured via env vars (full table in the [Scheduled Tasks reference](../reference/scheduled-tasks.md)): +Configured via env vars (full table in [Scheduled Tasks](scheduled-tasks.md)): | Variable | Default | Purpose | | -------------------- | ----------- | ------------------------------------------------------------------------------------------------- | diff --git a/docs/administration/scheduled-tasks.md b/docs/administration/scheduled-tasks.md index 0f9922af..a524460a 100644 --- a/docs/administration/scheduled-tasks.md +++ b/docs/administration/scheduled-tasks.md @@ -1,6 +1,6 @@ --- title: Scheduled Tasks -description: What RomM runs in the background, how to reschedule it, and how to trigger on demand. +description: Runs tasks in the background, reschedule and trigger them on demand --- # Scheduled Tasks @@ -9,10 +9,8 @@ RomM runs background work through **RQ** (Redis Queue). Tasks fall into four cat - **Scheduled**: cron-driven, run on their own - **Watcher**: triggered by filesystem events -- **Manual**: admin-triggered from the UI or API -- **Enqueued**: side effects of user actions (a scan started from the Scan page, a metadata refresh on a ROM edit). Not listed here - -For the lookup-only reference (every task, its cron default, its env var), see [Scheduled Tasks Reference](../reference/scheduled-tasks.md). This page is the narrative: what each one is for, when to worry, and how to tune. +- **Manual**: user-triggered from the UI or API +- **Enqueued**: side effects of user actions (a scan on the `/scan` page, a metadata refresh on a ROM edit, etc.) ## The full table @@ -22,45 +20,22 @@ For the lookup-only reference (every task, its cron default, its env var), see [ Every scheduled task takes a standard 5-field cron expression: -```text -minute hour day-of-month month day-of-week -``` - -Examples: - - `0 3 * * *`: 3 AM daily - `0 */6 * * *`: every 6 hours, on the hour - `*/30 * * * *`: every 30 minutes - `0 2 * * 0`: 2 AM every Sunday -Set the env var, restart the container. Alembic runs on every start, and the scheduler picks up the new schedule the moment RomM comes back up. +Set the env var and restart the container; the scheduler picks up the new schedule the moment RomM comes back up. -## Disabling a scheduled task +## Enabling a scheduled task -Two ways: - -- **The clean way**: set the cron to a time that essentially never fires. `0 0 31 2 *` (Feb 31st) works. -- **The feature-flag way**: some tasks have a dedicated enable toggle, e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false` disables the LaunchBox sync. - -Check the [env var reference](../reference/environment-variables.md) for which toggles exist. +Each task has an `ENABLE_*` environment variable, like `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=true` which enables the LaunchBox sync. Check the [env var reference](../reference/environment-variables.md) for the full list. Some tasks are enabled by default, others aren't. ## Triggering a task manually -Three paths: - ### From the Administration page -**Administration → Tasks** shows every task with a Run button. Admins (anyone with `tasks.run` scope) can trigger: - -- Folder Scan (launches the Scan page with the task pre-started) -- Cleanup Missing ROMs -- Cleanup Orphaned Resources -- LaunchBox Metadata Sync -- Switch titleDB Fetch -- Image Conversion -- RetroAchievements Sync -- Netplay Cleanup -- Push-Pull Device Sync +**Administration → Tasks** shows every task with a "Run" button. Admins (anyone with `tasks.run` scope) can trigger: ### From the API @@ -69,64 +44,19 @@ POST /api/tasks/run/{task_name} Authorization: Bearer ``` -Full details in the [API Reference](../developers/api-reference.md). Useful for cron runs driven from outside RomM (e.g. an Ansible playbook). - -### Via RQ directly - -Advanced. Connect to Valkey, inspect the `rq` queues, enqueue a job manually. Only do this if you know what you're doing and have a reason the API doesn't serve. - -## What each task does - -### Folder Scan - -The nightly library scan. Defaults to **Quick** mode (skips files already in the DB) so it's cheap even on big libraries. See [Scanning & Watcher](scanning-and-watcher.md) for the full scan mechanic. - -### Switch titleDB Fetch - -Downloads an updated copy of the Nintendo Switch title ID database used for matching files with names like `0100000000010000.xci`. Weekly is plenty. - -### LaunchBox Metadata Sync - -Refreshes the local LaunchBox metadata cache used when LaunchBox is enabled as a metadata provider. Nightly at 2 AM by default. Only useful if `LAUNCHBOX_API_ENABLED=true`. Disable it otherwise to save a handful of CPU seconds. - -### Image Conversion - -Re-encodes fetched cover art, screenshots, and manuals to WebP for faster serving. Nightly at 3 AM. Idempotent, so safe to run more often if you're importing a lot of media. - -### RetroAchievements Sync - -Refreshes per-user RA progression. Nightly at 4 AM. Users see updated achievement counts on their next load. - -### Netplay Cleanup - -Sweeps orphaned netplay sessions: rooms where every participant has disconnected but the metadata lingers. Every 30 minutes by default. - -### Push-Pull Device Sync - -Bidirectional sync with registered devices (handhelds, etc.). Every 15 minutes by default. Disabled implicitly if no devices are registered or if `SSH_PRIVATE_KEY_PATH` isn't configured. See [SSH Sync](ssh-sync.md). - -### Cleanup Missing ROMs (manual) - -Walks the DB, checks each ROM's file still exists on disk, drops entries whose files are gone. Run after major library moves. - -### Cleanup Orphaned Resources (manual) - -Deletes cover images, screenshots, and manuals no longer referenced by any ROM. Safe to run any time, and can free tens of GB if you've been churning the library. - ## Monitoring tasks - **Live**: Administration → Tasks page shows every task's current status (queued, running, idle, failed). - **API**: `GET /api/tasks/status` for a JSON summary. Wire this to an uptime monitor if you want alerts. - **Logs**: `docker logs romm` → look for `rq.worker` lines. -- **Heartbeat**: `GET /api/heartbeat` returns overall health plus per-task summary, handy for monitoring dashboards. -A task that's been "running" for hours is usually a scan that hit `SCAN_TIMEOUT_HOURS`, and the log will say so. Tasks that fail leave a stack trace in the container logs, and the RQ `failed` queue retains the last few for inspection. +A task that's been "running" for hours is usually a scan that hit `SCAN_TIMEOUT_HOURS`, and the logs will say so. Tasks that fail leave a stack trace in the container logs, and the RQ `failed` queue retains the last few for inspection. ## Tuning for small hosts -Defaults assume you've got a reasonable box. On a Pi or NAS with 2 GB of RAM and a single core: +On a Raspberry Pi or NAS with 2 GB of RAM and/or a single CPU core: -- Raise the cron intervals (daily → weekly) for the nightlies. -- Set `SCAN_WORKERS=1` to avoid concurrent scan processes. -- Enable the watcher but raise `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` to 60+ seconds: slower reaction, far less churn. -- Disable image conversion if you don't care about WebP (`IMAGE_CONVERSION_INTERVAL_CRON=0 0 31 2 *`). +- Raise the cron intervals (daily → weekly) for the nightlies +- Set `SCAN_WORKERS=1` to avoid concurrent scan processes +- Enable the watcher but raise `RESCAN_ON_FILESYSTEM_CHANGE_DELAY` to 60+ seconds +- Disable image conversion if you don't care about WebP (`ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP=false`). diff --git a/docs/developers/index.md b/docs/developers/index.md index 0208313a..47ef5df9 100644 --- a/docs/developers/index.md +++ b/docs/developers/index.md @@ -34,7 +34,6 @@ Looking for end-user or operator content? See [Using RomM](../using/index.md) or - **[Environment Variables](../reference/environment-variables.md):** every env var - **[Configuration File](../reference/configuration-file.md):** `config.yml` schema -- **[Scheduled Tasks](../reference/scheduled-tasks.md):** background job reference - **[Exports](../reference/exports.md):** gamelist.xml / Pegasus export formats - **[Feeds](../reference/feeds.md):** every URL-feed endpoint (Tinfoil, pkgj, WebRcade, etc.) - **[Glossary](../reference/glossary.md):** canonical terminology diff --git a/docs/reference/environment-variables.md b/docs/reference/environment-variables.md index 740dd190..fadc96d1 100644 --- a/docs/reference/environment-variables.md +++ b/docs/reference/environment-variables.md @@ -66,6 +66,6 @@ For metadata providers (IGDB, ScreenScraper, etc.) see [Metadata Providers](../a ## See also - [Configuration File](configuration-file.md): everything that lives in `config.yml` rather than env vars -- [Scheduled Tasks](scheduled-tasks.md): cron-controlling env vars in context +- [Scheduled Tasks](../administration/scheduled-tasks.md): cron-controlling env vars in context - [Authentication](../administration/authentication.md): auth-related env vars in narrative form - [Metadata Providers](../administration/metadata-providers.md): per-provider credential env vars diff --git a/docs/reference/scheduled-tasks.md b/docs/reference/scheduled-tasks.md deleted file mode 100644 index 11850716..00000000 --- a/docs/reference/scheduled-tasks.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Scheduled Tasks -description: Every background task RomM runs, with default schedule, env var, and purpose. ---- - -# Scheduled Tasks - -RomM runs background work via **RQ** (Redis Queue). Tasks fall into three categories: - -- **Scheduled**: cron-driven, fire on their own -- **Watcher**: triggered by filesystem events -- **Manual**: admin-triggered from the UI or API - -This page is the **lookup reference**: every task, its cron default, its env var, and a one-line purpose. For the narrative (how to tune cadence, how to trigger manually, how to monitor), see [Administration → Scheduled Tasks](../administration/scheduled-tasks.md). - -## Full task table - ---8<-- "scheduled-tasks.md" - -## Setting the cadence - -Every scheduled task takes a standard 5-field cron expression: - -```text -minute hour day-of-month month day-of-week -``` - -Examples: - -- `0 3 * * *`: 3 AM daily -- `0 */6 * * *`: every 6 hours -- `*/30 * * * *`: every 30 minutes -- `0 2 * * 0`: 2 AM every Sunday - -Set the matching env var from the table above, restart the container, and the scheduler picks up the new cadence on startup. - -## Disabling a task - -Two approaches: - -- **Dedicated enable flag.** Some tasks have a boolean toggle (e.g. `ENABLE_SCHEDULED_UPDATE_LAUNCHBOX_METADATA=false`). Cleanest -- **Cron-it-off.** Set the cron to a moment that effectively never fires. `0 0 31 2 *` (Feb 31st) works. - -## Where the table comes from - -Generated by `scripts/gen_scheduled_tasks.py` against the task registry at the SHA pinned in [`scripts/sources.toml`](https://github.com/rommapp/docs/blob/master/scripts/sources.toml). When RomM adds or renames a task, the next docs release regenerates the table. - -## See also - -- [Administration → Scheduled Tasks](../administration/scheduled-tasks.md): full narrative, per-task purpose, monitoring, tuning -- [Environment Variables](environment-variables.md): all task-related env vars in context -- [Scanning & Watcher](../administration/scanning-and-watcher.md): the scan and watcher tasks in depth -- [SSH Sync](../administration/ssh-sync.md): the push-pull sync task in depth diff --git a/mkdocs.yml b/mkdocs.yml index 56b55585..0075fe63 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -104,7 +104,7 @@ plugins: # Tools → Ecosystem Tools/Igir-Collection-Manager.md: ecosystem/igir.md # Maintenance - Maintenance/Scheduled-Tasks.md: reference/scheduled-tasks.md + Maintenance/Scheduled-Tasks.md: administration/scheduled-tasks.md Maintenance/Migrating-to-new-machine.md: install/backup-and-restore.md Maintenance/Upgrading-to-3.0.md: releases/upgrading-to-3.0.md # Integrations → Ecosystem From 667726f8d52537e9661f618a83da0734d0469fb9 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 25 Apr 2026 09:38:33 -0400 Subject: [PATCH 051/121] finish server stats --- docs/administration/scanning-and-watcher.md | 20 ++++---- docs/administration/server-stats.md | 51 +++++---------------- 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/docs/administration/scanning-and-watcher.md b/docs/administration/scanning-and-watcher.md index 08b71032..4fac6f21 100644 --- a/docs/administration/scanning-and-watcher.md +++ b/docs/administration/scanning-and-watcher.md @@ -17,14 +17,14 @@ All three share the same scan engine and the same set of **scan modes**. Every scan picks one mode. Modes differ in what they touch, so use the most-targeted mode that accomplishes what you want. -| Mode | What it does | When to use | -| ----------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set (very fast). | -| **Quick** | Skips files that already exist in the DB, with no metadata refresh. | Default for scheduled runs and the watcher. | -| **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | -| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured). | -| **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash or when you suspect file corruption. | -| **Complete** | Full rescan, recalculating hashes and re-fetching metadata for everything. | Rarely, since it takes a long time. | +| Mode | What it does | When to use | +| ----------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| **New Platforms** | Only scans platform folders not already in the DB. | After mounting a new ROM set (very fast). | +| **Quick** | Skips files that already exist in the DB, with no metadata refresh. | Default for scheduled runs and the watcher. | +| **Unmatched** | Re-runs metadata matching against ROMs currently missing external IDs. | After adding a new metadata provider, or when some titles didn't match on the first scan. | +| **Update** | Re-fetches metadata for all already-matched ROMs. | When metadata providers have meaningfully changed (e.g. IGDB restructured). | +| **Hashes** | Recalculates CRC/MD5/SHA1 hashes. | After upgrading from a version that didn't hash or when you suspect file corruption. | +| **Complete** | Full rescan, recalculating hashes and re-fetching metadata for everything. | Rarely, since it takes a long time. | You can further scope a scan to specific **platforms** and specific **metadata providers**, useful when only one provider has changed (e.g. just enabled Hasheous → Unmatched scan, Hasheous selected, on all platforms). @@ -48,8 +48,8 @@ Configured via env vars (full table in [Scheduled Tasks](scheduled-tasks.md)): | -------------------- | ----------- | ------------------------------------------------------------------------------------------------- | | `SCAN_INTERVAL_CRON` | `0 0 * * *` | Cron expression for the scheduled library scan. Runs a **Quick** scan by default. | | `SCAN_TIMEOUT_HOURS` | `1` | Hard cap: scans that exceed this are killed and logged. | -| `SCAN_WORKERS` | `1` | Concurrent worker processes for scanning; leave as auto unless you're tuning. | -| `SEVEN_ZIP_TIMEOUT` | `60` | Per-archive timeout for `.7z` extraction during scan; raise if scanning huge compressed ROM sets. | +| `SCAN_WORKERS` | `1` | Concurrent worker processes for scanning; leave as auto unless you're tuning. | +| `SEVEN_ZIP_TIMEOUT` | `60` | Per-archive timeout for `.7z` extraction during scan; raise if scanning huge compressed ROM sets. | To disable scheduled scans entirely, either unset the cron or set it to something unreachable (`SCAN_INTERVAL_CRON=0 0 31 2 *`). diff --git a/docs/administration/server-stats.md b/docs/administration/server-stats.md index 42bcea39..613e5589 100644 --- a/docs/administration/server-stats.md +++ b/docs/administration/server-stats.md @@ -1,11 +1,11 @@ --- title: Server Stats -description: Read the Server Stats admin page and know what the numbers mean. +description: "The numbers Mason! What do they mean?" --- # Server Stats -**Administration → Server Stats.** Admin-only page that reports what's on disk and in the catalogue. Useful for capacity planning, spotting a runaway platform, and verifying a scan actually persisted data. +**Administration → Server Stats** is an admin-only page that reports what's on disk and in the catalogue. ## What's shown @@ -18,45 +18,18 @@ description: Read the Server Stats admin page and know what the numbers mean. | **Saves** | User save files across all users. | | **States** | Emulator save states across all users. | | **Screenshots** | User-uploaded screenshots. Provider-fetched screenshots aren't counted here. | -| **Firmware** | Uploaded BIOS / firmware files. | - -### Storage footprint - -A breakdown of disk usage by directory: - -| Bucket | Maps to | Grows when | -| ------------- | ----------------- | ----------------------------------------------------------------------------------------- | -| **Library** | `/romm/library` | You add ROMs. Controlled by you, not RomM. | -| **Resources** | `/romm/resources` | Scans fetch cover art, screenshots, manuals. Safe to purge, can be rebuilt from a rescan. | -| **Assets** | `/romm/assets` | Users upload saves, states, screenshots. **Back this up.** | -| **Config** | `/romm/config` | Rarely: you or admins edit `config.yml`. | +| **Size on disk** | Total disk usage of all ROMs, saves, states, and screenshots. | ### Per-platform breakdown -Under the summary, an expandable table sorted by either ROM count or disk usage. Click a platform to drill into: +Under the summary, it's a table sorted by name, size or game count. For each platform, you can see: -- Matched / unmatched count +- Game count +- Size on disk (in bytes and by percentage of total) - Region distribution (how many games tagged USA, Japan, Europe, World, etc.) -- Language distribution -- Metadata coverage: how many games have each field populated (cover, description, release date, rating) - -Useful for: "which platform is eating my disk?" and "which platform has the worst match rate?" - - -!!! note "Per-platform stats are opt-in" - Computing per-platform stats walks the whole DB and costs real time on big libraries. The main stats load fast, and per-platform expansion loads on demand. - -## Using stats for capacity planning - -Rule-of-thumb sizes: - -| Ratio | Typical value | -| ------------------- | ------------------------------------------------------------------------------------------------ | -| Resources / Library | 2-5% on average, higher if you've enabled many metadata providers or run Image Conversion often. | -| Assets / Library | <1% unless you have lots of heavy PSP/PS3 saves. | -| DB size / Games | ~10-50 KB per ROM row, depending on metadata richness. | +- Metadata coverage (how many games have metadata from each provider) -If **Resources** is ballooning, run the [Cleanup Orphaned Resources](scheduled-tasks.md#cleanup-orphaned-resources-manual) task. If **Assets** is growing unexpectedly, a user is probably uploading save states for long-form JRPGs. That's fine, just plan for it. +When you want to know "which platform is eating my disk?" or "which platform has the worst match rate?" ## API @@ -72,8 +45,8 @@ Wire to your monitoring stack via the API rather than scraping the HTML page. Se ## Troubleshooting -- **Numbers look stale**: stats are computed on page load, not cached. Reload. If still stale, the DB connection is degraded. Check `docker logs romm 2>&1 | grep -i database`. -- **Disk sizes look wrong**: RomM reports what it sees in `/romm/*`. If your compose mounts a path that's smaller than the host dataset (e.g. you mounted a sub-directory), RomM only sees that subset. -- **"Platform stats couldn't load"**: the DB query timed out. On very large libraries this happens. Retry, or raise `SCAN_TIMEOUT_HOURS` (yes, it also gates the stats query). +- **Numbers look stale**: stats are computed on page load, not cached +- **Disk sizes look wrong**: if your compose mounts a path that's smaller than the host dataset (e.g. you mounted a sub-directory), it will only sees that subset +- **"Platform stats couldn't load"**: the DB query timed out on a very large library -For anything else: [Troubleshooting](../troubleshooting/index.md). +For anything else, see [Troubleshooting](../troubleshooting/index.md). From 65fa38091198311335656cc1d8ba1a6520ddc389 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 25 Apr 2026 10:43:27 -0400 Subject: [PATCH 052/121] finish administration --- docs/Navigation.md | 2 +- docs/administration/index.md | 1 - docs/administration/users-and-roles.md | 22 +++++++------------ docs/ecosystem/device-sync-protocol.md | 6 ++--- docs/ecosystem/grout.md | 4 ++-- docs/ecosystem/index.md | 1 + .../{administration => ecosystem}/ssh-sync.md | 10 ++++----- docs/troubleshooting/sync.md | 4 ++-- docs/using/saves-and-states.md | 2 +- 9 files changed, 23 insertions(+), 29 deletions(-) rename docs/{administration => ecosystem}/ssh-sync.md (90%) diff --git a/docs/Navigation.md b/docs/Navigation.md index d68c75eb..cd7f1277 100644 --- a/docs/Navigation.md +++ b/docs/Navigation.md @@ -41,7 +41,6 @@ search: - [Server Stats](administration/server-stats.md) - [Observability](administration/observability.md) - [Firmware Management](administration/firmware-management.md) - - [SSH Sync](administration/ssh-sync.md) - [Administration Page](administration/administration-page.md) - Using RomM - [Overview](using/index.md) @@ -85,6 +84,7 @@ search: - For developers - [Device Sync Protocol](ecosystem/device-sync-protocol.md) - [Client API Tokens](ecosystem/client-api-tokens.md) + - [SSH Sync](ecosystem/ssh-sync.md) - [Community Apps](ecosystem/community-apps.md) - [Igir Collection Manager](ecosystem/igir.md) - API & Development diff --git a/docs/administration/index.md b/docs/administration/index.md index 0b997931..649a035b 100644 --- a/docs/administration/index.md +++ b/docs/administration/index.md @@ -29,7 +29,6 @@ The end-user equivalent (how to actually play the games, build collections, uplo - **[Scheduled Tasks](scheduled-tasks.md)**: what runs in the background and how to tune it - **[Server Stats](server-stats.md)**: the stats page and what its numbers mean - **[Observability](observability.md)**: logs, Sentry, OpenTelemetry, `/heartbeat` -- **[SSH Sync](ssh-sync.md)**: push/pull sync to handhelds and other devices - **[Administration Page](administration-page.md)**: the in-app admin UI tour ### Configuration diff --git a/docs/administration/users-and-roles.md b/docs/administration/users-and-roles.md index 3da82ef2..bc42c3f7 100644 --- a/docs/administration/users-and-roles.md +++ b/docs/administration/users-and-roles.md @@ -12,7 +12,7 @@ RomM is multi-user from the start. The first user created during Setup is always | Role | Who it's for | Default scopes | | ---------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | **Admin** | You, and anyone you fully trust. | All scopes, including user management and task execution. | -| **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload. No user management. | +| **Editor** | Household members who help curate the library. | Read everything, edit ROMs/platforms/collections, upload, but no user management. | | **Viewer** | Guests, kids, anyone who should only play and track their own progress. | Read the library, manage their own saves/states/screenshots/profile. | Roles are a convenience layer on top of **scopes** (see the scope matrix below for exactly what each role grants). You can't create custom roles (yet), so if you need finer-grained access, use the most restrictive role and rely on [Client API Tokens](../ecosystem/client-api-tokens.md) for per-app customisation. @@ -45,8 +45,6 @@ RomM authorisation is scope-based. Every API call and UI action maps to one or m ## Creating users -Two ways: - ### Admin adds directly **Administration → Users → Add.** Set username, email, password, and role, and the account is usable immediately. @@ -56,24 +54,20 @@ Two ways: Better when you don't want to handle someone else's password. 1. **Administration → Users → Invite.** Pick a role, and RomM generates a single-use invite link. -2. Send the link, and the recipient opens it, picks their own username and password, and is logged in. -3. Invite links expire: the default is 30 days, configurable via [`INVITE_TOKEN_DAYS`](../reference/environment-variables.md). - -### Public self-registration - -Off by default. To let anyone with the URL register their own Viewer account, set `ALLOW_PUBLIC_REGISTRATION=true`. Only enable this if your instance is behind auth at the reverse-proxy layer (Authelia, etc.) or you genuinely want open registration: once on, anyone who reaches `/register` can create an account. +2. Send the link and the recipient opens it, picks their own username and password, and is logged in. +3. Invite links expire after 600 seconds by default, configurable via [`INVITE_TOKEN_EXPIRY_SECONDS`](../reference/environment-variables.md). ### OIDC -If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md): look for `OIDC_CLAIM_ROLES` and the per-role env vars. +If you've wired up OIDC, new identities can be provisioned on first login. Role mapping from OIDC claims is covered in [OIDC Setup](oidc/index.md). ## Editing and deleting users -- **Change role**: Admin → Users → Edit → Role dropdown, taking effect on next login -- **Reset password**: Admin → Users → Edit → New password. For self-service, the user can use the "Forgot password" flow from the login page if email is configured. -- **Delete**: Admin → Users → red delete icon → confirm. RomM won't let you delete the last admin or delete yourself while signed in. +- **Change role**: Admin → Users → Edit → Role dropdown, taking effect on next login. +- **Reset password**: Admin → Users → Edit → New password. +- **Delete**: Admin → Users → Delete → Confirm. Note that you can't delete the last admin or delete yourself while signed in. -Deleting a user keeps their contributions (collections they made public, ROM metadata edits) but removes their personal data (per-ROM ratings, saves, states, play sessions, paired devices, API tokens). +Deleting a user keeps their contributions (collections they made public, ROM metadata edits) but removes their personal data from the database (per-ROM ratings, saves, states, play sessions, paired devices, API tokens) but does not delete any files from disk. ## API tokens (advanced) diff --git a/docs/ecosystem/device-sync-protocol.md b/docs/ecosystem/device-sync-protocol.md index 7a11c931..6426368a 100644 --- a/docs/ecosystem/device-sync-protocol.md +++ b/docs/ecosystem/device-sync-protocol.md @@ -7,7 +7,7 @@ description: Wire-level reference for RomM's save/state/play-session sync protoc This page documents the **wire-level protocol** RomM uses for bidirectional sync between itself and companion apps. If you're building a companion app, this is your reference. -End users: this is under the hood. You don't need to read it. See [Saves & States](../using/saves-and-states.md) and [SSH Sync](../administration/ssh-sync.md) for operator/user views. +End users: this is under the hood. You don't need to read it. See [Saves & States](../using/saves-and-states.md) and [SSH Sync](ssh-sync.md) for operator/user views. ## The three primitives @@ -221,7 +221,7 @@ Send up to 100 per request. ## SSH-based sync (alternative) -For handhelds where the RomM server connects over SSH instead of the device polling HTTPS, see [SSH Sync](../administration/ssh-sync.md). The RomM-side push-pull task reads the device's filesystem over SSH rather than going through API calls. Same data, different transport. +For handhelds where the RomM server connects over SSH instead of the device polling HTTPS, see [SSH Sync](ssh-sync.md). The RomM-side push-pull task reads the device's filesystem over SSH rather than going through API calls. Same data, different transport. Companion apps generally prefer the API model. SSH-based sync exists mostly for cases where the device can't run a TLS client reliably. @@ -242,5 +242,5 @@ Currently polling-only. Companion apps check `/api/sync/negotiate` periodically - [Client API Tokens](client-api-tokens.md): auth + pairing - [API Authentication](../developers/api-authentication.md): general auth primer - [API Reference](../developers/api-reference.md): full endpoint catalogue -- [SSH Sync](../administration/ssh-sync.md): alternative transport for handhelds +- [SSH Sync](ssh-sync.md): alternative transport for handhelds - [Argosy](argosy.md), [Grout](grout.md): reference client implementations diff --git a/docs/ecosystem/grout.md b/docs/ecosystem/grout.md index b2213e01..da72dfee 100644 --- a/docs/ecosystem/grout.md +++ b/docs/ecosystem/grout.md @@ -91,7 +91,7 @@ Scope the token narrowly when creating: default scopes are fine for most users b ## SSH sync (operator-side) -If you want Grout to pull from RomM over SSH rather than HTTPS (e.g. on a trusted LAN with no reverse proxy) see [SSH Sync](../administration/ssh-sync.md) for the server-side config. Grout supports both modes, selectable in Settings → Connection. +If you want Grout to pull from RomM over SSH rather than HTTPS (e.g. on a trusted LAN with no reverse proxy) see [SSH Sync](ssh-sync.md) for the server-side config. Grout supports both modes, selectable in Settings → Connection. ## Troubleshooting @@ -106,5 +106,5 @@ More in [Device Sync Troubleshooting](../troubleshooting/sync.md). - [Client API Tokens](client-api-tokens.md): token and pairing flow reference - [Device Sync Protocol](device-sync-protocol.md): wire-level protocol -- [SSH Sync](../administration/ssh-sync.md): operator-side SSH config +- [SSH Sync](ssh-sync.md): operator-side SSH config - [rommapp/grout](https://github.com/rommapp/grout): source, issues, releases diff --git a/docs/ecosystem/index.md b/docs/ecosystem/index.md index f01f4d55..5993d619 100644 --- a/docs/ecosystem/index.md +++ b/docs/ecosystem/index.md @@ -54,6 +54,7 @@ For developers building something new on top of RomM: - **[Client API Tokens](client-api-tokens.md)**: how to authenticate your app, how the device-pairing flow works - **[Device Sync Protocol](device-sync-protocol.md)**: wire-level reference for save/state/play-session sync +- **[SSH Sync](ssh-sync.md)**: server-side SSH config for push/pull sync to handhelds - **[API Reference](../developers/api-reference.md)**: every REST endpoint - **[WebSockets](../developers/websockets.md)**: live-update channels and Netplay coordination - **[Consuming OpenAPI](../developers/openapi.md)**: codegen patterns diff --git a/docs/administration/ssh-sync.md b/docs/ecosystem/ssh-sync.md similarity index 90% rename from docs/administration/ssh-sync.md rename to docs/ecosystem/ssh-sync.md index 01d75b92..95e4578a 100644 --- a/docs/administration/ssh-sync.md +++ b/docs/ecosystem/ssh-sync.md @@ -1,13 +1,13 @@ --- title: SSH Sync -description: Configure SSH key-based push/pull sync to handhelds and other devices. +description: Configure SSH key-based push/pull sync --- # SSH Sync RomM's Push-Pull Device Sync task can push saves/states to registered devices and pull them back after a session, over SSH, using a key that RomM holds. This page covers the server-side setup. -The client side (a handheld running Grout, a SteamDeck running DeckRommSync, etc.) lives in [Integrations & Ecosystem](../ecosystem/index.md). The wire protocol (API-level sync negotiation, play-session ingest) lives in [Device Sync Protocol](../ecosystem/device-sync-protocol.md). +The client side (a handheld running Grout, a SteamDeck running DeckRommSync, etc.) lives in [Integrations & Ecosystem](index.md). The wire protocol (API-level sync negotiation, play-session ingest) lives in [Device Sync Protocol](device-sync-protocol.md). !!! note "Most companion apps don't need this" @@ -66,7 +66,7 @@ Copy the **public** key (`~/romm-sync-key.pub` from step 1 above) to the device' 2. Register the device in RomM -Device registration is done through a companion app (typically Grout itself) using the [Client API Token pairing flow](../ecosystem/client-api-tokens.md). Once registered, RomM knows: +Device registration is done through a companion app (typically Grout itself) using the [Client API Token pairing flow](client-api-tokens.md). Once registered, RomM knows: - Device name + type - Hostname / IP @@ -93,7 +93,7 @@ The **Push-Pull Device Sync** scheduled task (default: every 15 minutes) does, f 4. Downloads (pull) anything newer on the device. 5. Updates play session records if the device reports any. -Tune the schedule via `PUSH_PULL_SYNC_INTERVAL_CRON`. See [Scheduled Tasks](scheduled-tasks.md). +Tune the schedule via `PUSH_PULL_SYNC_INTERVAL_CRON`. See [Scheduled Tasks](../administration/scheduled-tasks.md). Disable sync for a specific device by deregistering it from **Administration → Devices**, or disable the task entirely by unsetting / neutering `PUSH_PULL_SYNC_INTERVAL_CRON`. @@ -101,7 +101,7 @@ Disable sync for a specific device by deregistering it from **Administration → - **`Permission denied (publickey)`**: authorised key isn't set up on the device, or the private key inside the container can't be read (check the file permissions and bind-mount flags). - **`Host key verification failed`**: the device's host key changed (after a reinstall, typically). Shell into the container and remove the offending line from `~/.ssh/known_hosts`. -- **Sync silently doesn't run**: check `GET /api/tasks/status` for the Push-Pull task's state. "failed" points you at the error, and "never ran" means the cron isn't firing (see [Scheduled Tasks](scheduled-tasks.md)). +- **Sync silently doesn't run**: check `GET /api/tasks/status` for the Push-Pull task's state. "failed" points you at the error, and "never ran" means the cron isn't firing (see [Scheduled Tasks](../administration/scheduled-tasks.md)). - **Connection times out**: the device is offline or the network path is blocked. Confirm reachability from the RomM container: `docker exec romm ping `. More at [Device Sync Troubleshooting](../troubleshooting/sync.md). diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md index 09773a89..ba23855a 100644 --- a/docs/troubleshooting/sync.md +++ b/docs/troubleshooting/sync.md @@ -54,7 +54,7 @@ RomM's SSH key isn't authorised on the device. 2. Check line breaks (CRLF vs LF issues bite here). The `authorized_keys` file should have one key per line, Unix line endings. 3. Check file permissions on the device: `~/.ssh/` should be `700`, `~/.ssh/authorized_keys` should be `600`. -See [SSH Sync → Configuring a device](../administration/ssh-sync.md#configuring-a-device). +See [SSH Sync → Configuring a device](../ecosystem/ssh-sync.md#configuring-a-device). ### Host key verification failed @@ -122,5 +122,5 @@ Admins can force a re-eval via Administration → Tasks → Refresh Smart Collec - [Client API Tokens](../ecosystem/client-api-tokens.md): token + pairing flow reference - [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level protocol details -- [SSH Sync](../administration/ssh-sync.md): server-side SSH sync config +- [SSH Sync](../ecosystem/ssh-sync.md): server-side SSH sync config - [Companion apps](../ecosystem/community-apps.md): list of integrations and their status diff --git a/docs/using/saves-and-states.md b/docs/using/saves-and-states.md index af80fc3a..9e45c4de 100644 --- a/docs/using/saves-and-states.md +++ b/docs/using/saves-and-states.md @@ -71,7 +71,7 @@ For bulk cleanup (e.g. "delete every state for games I've beaten"), use the mult Saves and states can sync to/from registered devices (Grout on muOS, DeckRommSync on a Deck, etc.). Covered in depth in the ecosystem section: - [Device Sync Protocol](../ecosystem/device-sync-protocol.md): wire-level reference -- [SSH Sync](../administration/ssh-sync.md): operator-side config +- [SSH Sync](../ecosystem/ssh-sync.md): operator-side config - [Argosy Launcher](../ecosystem/argosy.md) / [Grout](../ecosystem/grout.md): per-app setup From the end-user side: once your device is paired and sync is running, saves made on the device appear in RomM within a couple of sync cycles (default: 15 minutes). Conflicts (same ROM saved on two devices between syncs) surface as two separate save entries, so pick which to keep. From 8cf6cf3176cbd920ea127e0efb9f60e37d0753dc Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 25 Apr 2026 18:16:37 -0400 Subject: [PATCH 053/121] finish api auth --- docs/administration/authentication.md | 2 +- docs/administration/observability.md | 2 +- docs/administration/oidc/authelia.md | 6 ++--- docs/administration/oidc/authentik.md | 6 ++--- docs/administration/oidc/index.md | 4 +-- docs/administration/oidc/keycloak.md | 10 +++---- docs/administration/oidc/pocketid.md | 6 ++--- docs/administration/oidc/zitadel.md | 8 +++--- docs/developers/api-authentication.md | 36 ++++++++++++-------------- docs/developers/api-reference.md | 4 +-- docs/developers/index.md | 4 +-- docs/developers/openapi.md | 8 +++--- docs/developers/websockets.md | 4 +-- docs/ecosystem/argosy.md | 2 +- docs/ecosystem/muos-app.md | 2 +- docs/ecosystem/playnite-plugin.md | 6 ++--- docs/index.md | 2 +- docs/install/kubernetes.md | 6 ++--- docs/reference/feeds.md | 4 +-- docs/reference/ports-and-endpoints.md | 2 +- docs/troubleshooting/authentication.md | 2 +- docs/troubleshooting/sync.md | 4 +-- docs/using/console-mode.md | 2 +- docs/using/downloads.md | 2 +- 24 files changed, 65 insertions(+), 69 deletions(-) diff --git a/docs/administration/authentication.md b/docs/administration/authentication.md index 6b78edfc..ee801c87 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication.md @@ -62,7 +62,7 @@ environment: - OIDC_CLIENT_ID=... - OIDC_CLIENT_SECRET=... - OIDC_SERVER_APPLICATION_URL=https://auth.example.com - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_REDIRECT_URI=https://demo.romm.app/api/oauth/openid ``` When OIDC is configured, the login page shows an "OIDC" button. Set `OIDC_AUTOLOGIN=true` to redirect straight to the IdP without the user having to click it. diff --git a/docs/administration/observability.md b/docs/administration/observability.md index 608464c1..d31d0d5c 100644 --- a/docs/administration/observability.md +++ b/docs/administration/observability.md @@ -55,7 +55,7 @@ Wire this to your uptime monitor. A failure here means that the process is down ```bash # Basic uptime check -curl -fsS https://romm.example.com/api/heartbeat > /dev/null \ +curl -fsS https://demo.romm.app/api/heartbeat > /dev/null \ && echo "up" \ || echo "down" ``` diff --git a/docs/administration/oidc/authelia.md b/docs/administration/oidc/authelia.md index 08417e97..5254307f 100644 --- a/docs/administration/oidc/authelia.md +++ b/docs/administration/oidc/authelia.md @@ -50,7 +50,7 @@ identity_providers: grant_types: - authorization_code redirect_uris: - - "https://romm.example.com/api/oauth/openid" + - "https://demo.romm.app/api/oauth/openid" claims_policy: "with_email" # must match the policy name above scopes: - openid @@ -73,9 +73,9 @@ environment: - OIDC_PROVIDER=authelia - OIDC_CLIENT_ID= - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_REDIRECT_URI=https://demo.romm.app/api/oauth/openid - OIDC_SERVER_APPLICATION_URL=https://auth.example.com - - ROMM_BASE_URL=https://romm.example.com + - ROMM_BASE_URL=https://demo.romm.app ``` `OIDC_REDIRECT_URI` must match what you put in `redirect_uris` exactly (scheme, host, path, no trailing slash). For role mapping from Authelia groups, see [OIDC Setup → Role mapping](index.md#role-mapping). diff --git a/docs/administration/oidc/authentik.md b/docs/administration/oidc/authentik.md index c895ca8e..a636f5fc 100644 --- a/docs/administration/oidc/authentik.md +++ b/docs/administration/oidc/authentik.md @@ -52,7 +52,7 @@ Configure: - **Name**: `RomM OIDC Provider` - **Authorization flow**: implicit consent -- **Redirect URIs**: `https://romm.example.com/api/oauth/openid` +- **Redirect URIs**: `https://demo.romm.app/api/oauth/openid` Copy the generated **Client ID** and **Client Secret**. You'll use them as `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` on the RomM side. @@ -82,9 +82,9 @@ environment: - OIDC_PROVIDER=authentik - OIDC_CLIENT_ID= - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_REDIRECT_URI=https://demo.romm.app/api/oauth/openid - OIDC_SERVER_APPLICATION_URL=https://auth.example.com/application/o/romm - - ROMM_BASE_URL=https://romm.example.com + - ROMM_BASE_URL=https://demo.romm.app ``` Note that `OIDC_SERVER_APPLICATION_URL` points at the per-application URL (`/application/o/`), not the Authentik root. diff --git a/docs/administration/oidc/index.md b/docs/administration/oidc/index.md index 0a82698e..56228739 100644 --- a/docs/administration/oidc/index.md +++ b/docs/administration/oidc/index.md @@ -42,8 +42,8 @@ environment: - OIDC_CLIENT_ID= - OIDC_CLIENT_SECRET= - OIDC_SERVER_APPLICATION_URL=https://auth.example.com - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid - - ROMM_BASE_URL=https://romm.example.com # must match your reverse-proxy URL + - OIDC_REDIRECT_URI=https://demo.romm.app/api/oauth/openid + - ROMM_BASE_URL=https://demo.romm.app # must match your reverse-proxy URL ``` `OIDC_REDIRECT_URI` must exactly match what you register at the provider (same scheme, host, path, no trailing slash). diff --git a/docs/administration/oidc/keycloak.md b/docs/administration/oidc/keycloak.md index 7710173d..222dfe23 100644 --- a/docs/administration/oidc/keycloak.md +++ b/docs/administration/oidc/keycloak.md @@ -25,9 +25,9 @@ In the Admin Console, select your realm → **Clients** → **Create client**. - Leave only **Standard flow** enabled - Click **Next** 5. Set URLs: - - **Root URL**: `https://romm.example.com` - - **Valid Redirect URIs**: `https://romm.example.com/api/oauth/openid` - - **Web origins**: `https://romm.example.com` + - **Root URL**: `https://demo.romm.app` + - **Valid Redirect URIs**: `https://demo.romm.app/api/oauth/openid` + - **Web origins**: `https://demo.romm.app` 6. Save, then head to the **Credentials** tab and copy the **Client Secret**. ## 3. Configure RomM @@ -38,9 +38,9 @@ environment: - OIDC_PROVIDER=keycloak - OIDC_CLIENT_ID=romm - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_REDIRECT_URI=https://demo.romm.app/api/oauth/openid - OIDC_SERVER_APPLICATION_URL=https://keycloak.example.com/realms/ - - ROMM_BASE_URL=https://romm.example.com + - ROMM_BASE_URL=https://demo.romm.app ``` `OIDC_SERVER_APPLICATION_URL` must include the realm (`.../realms/`), not just the Keycloak root. diff --git a/docs/administration/oidc/pocketid.md b/docs/administration/oidc/pocketid.md index d6006363..8fa371cd 100644 --- a/docs/administration/oidc/pocketid.md +++ b/docs/administration/oidc/pocketid.md @@ -19,7 +19,7 @@ In PocketID admin: 2. Go to **OIDC Client** → **Add OIDC Client**. 3. Fill in: - **Name**: `RomM` - - **Callback URLs**: `https://romm.example.com/api/oauth/openid` + - **Callback URLs**: `https://demo.romm.app/api/oauth/openid` 4. **Save**. Stay on this page as the client secret only displays **once**. 5. Copy both the Client ID and Client Secret now. @@ -31,9 +31,9 @@ environment: - OIDC_PROVIDER=pocket-id - OIDC_CLIENT_ID= - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_REDIRECT_URI=https://demo.romm.app/api/oauth/openid - OIDC_SERVER_APPLICATION_URL=https://id.example.com - - ROMM_BASE_URL=https://romm.example.com + - ROMM_BASE_URL=https://demo.romm.app ``` `OIDC_SERVER_APPLICATION_URL` is the root URL of your PocketID instance. diff --git a/docs/administration/oidc/zitadel.md b/docs/administration/oidc/zitadel.md index 5eee2957..ef4e1f5b 100644 --- a/docs/administration/oidc/zitadel.md +++ b/docs/administration/oidc/zitadel.md @@ -33,8 +33,8 @@ On the project's **General** tab, under **Applications**, click **New**. Tick ** - **Grant Types**: `Authorization Code` - **Response Types**: `Code` - **Authentication Method**: `Basic` -- **Redirect URIs**: `https://romm.example.com/api/oauth/openid` -- **Post Logout URIs**: `https://romm.example.com/` +- **Redirect URIs**: `https://demo.romm.app/api/oauth/openid` +- **Post Logout URIs**: `https://demo.romm.app/` Click **Create**. The **client secret is shown once**, copy it now! @@ -50,9 +50,9 @@ environment: - OIDC_PROVIDER=zitadel - OIDC_CLIENT_ID= - OIDC_CLIENT_SECRET= - - OIDC_REDIRECT_URI=https://romm.example.com/api/oauth/openid + - OIDC_REDIRECT_URI=https://demo.romm.app/api/oauth/openid - OIDC_SERVER_APPLICATION_URL=https://zitadel.example.com - - ROMM_BASE_URL=https://romm.example.com + - ROMM_BASE_URL=https://demo.romm.app ``` Zitadel's OIDC discovery URL is at `/.well-known/openid-configuration`, which is handy for debugging. diff --git a/docs/developers/api-authentication.md b/docs/developers/api-authentication.md index e9f839de..f3720253 100644 --- a/docs/developers/api-authentication.md +++ b/docs/developers/api-authentication.md @@ -1,11 +1,11 @@ --- title: API Authentication -description: How to authenticate to the RomM REST API. Session cookies, Basic, OAuth2 tokens, client tokens, and OIDC. +description: How to authenticate to the RomM API --- # API Authentication -RomM's REST API accepts four authentication modes. Pick the one that matches your client: +The API accepts multiple authentication modes: | Mode | Who it's for | How the credential is carried | | -------------------- | -------------------------------------------------------- | ----------------------------------------------------- | @@ -13,7 +13,6 @@ RomM's REST API accepts four authentication modes. Pick the one that matches you | **HTTP Basic** | Quick scripts, curl one-liners | `Authorization: Basic ` | | **OAuth2 Bearer** | Automation, CI, third-party apps | `Authorization: Bearer ` | | **Client API Token** | Companion apps (Argosy, Grout, Playnite, custom scripts) | `Authorization: Bearer rmm_` | -| **OIDC session** | Users who sign in via SSO | Same as session cookie but issued after OIDC callback | All of them resolve to the same scope model. See the [scope matrix in Users & Roles](../administration/users-and-roles.md#scope-matrix). A request is allowed if the active identity holds all scopes the endpoint requires. @@ -23,7 +22,7 @@ All of them resolve to the same scope model. See the [scope matrix in Users & Ro https:///api ``` -When RomM is behind a reverse proxy (as it should be in production), that's your public URL. When running locally without a proxy, the container listens on port `8080`. +When the app is behind a reverse proxy (as it should be when hosted in public), that's your public URL. When running locally without a proxy, the container listens on port `80`. ## Session login (browsers) @@ -34,7 +33,7 @@ Content-Type: application/x-www-form-urlencoded username=alice&password=s3cret ``` -Response sets a `session` cookie. Subsequent requests from the same browser are authenticated automatically. +Response sets a `romm_session` cookie, and subsequent requests from the same browser are authenticated automatically. Log out: @@ -46,22 +45,20 @@ For OIDC logins, hitting `/api/auth/logout` also triggers RP-Initiated Logout if ## HTTP Basic -Fine for quick scripts. Avoid in shared environments, because the credentials are sent on every request. - ```bash -curl -u alice:s3cret https://romm.example.com/api/roms +curl -u alice:s3cret https://demo.romm.app/api/roms ``` ```python import requests from requests.auth import HTTPBasicAuth -r = requests.get("https://romm.example.com/api/roms", +r = requests.get("https://demo.romm.app/api/roms", auth=HTTPBasicAuth("alice", "s3cret")) ``` ## OAuth2 Bearer token -The RomM backend implements the OAuth2 password grant. Exchange credentials for a short-lived access token and a refresh token: +The RomM backend implements the OAuth2 password grant, where you exchange credentials for a short-lived access token and a refresh token. Access and refresh token expiry times are configurable via `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` and `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS`. ```http POST /api/token @@ -73,13 +70,14 @@ grant_type=password&username=alice&password=s3cret&scope=roms.read%20roms.write ```json { "access_token": "eyJhbGciOi...", + "refresh_token": "eyJhbGciOi...", "token_type": "bearer", - "expires_in": 900, - "refresh_token": "eyJhbGciOi..." + "expires": 1800, + "refresh_expires": 604800, } ``` -Access tokens are HS256-signed JWTs valid for ~15 minutes. Send them as: +Access tokens are HS256-signed JWTs valid for `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` seconds. Send them as: ```http Authorization: Bearer eyJhbGciOi... @@ -94,7 +92,7 @@ Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=eyJhbGciOi... ``` -Request only the scopes you need. RomM will issue a token with the intersection of what you asked for and what the user has. +Request only the scopes you need and RomM will issue a token with the intersection of what you asked for and what the user has. ## Client API tokens (for companion apps) @@ -104,14 +102,14 @@ Token format: `rmm_` + 40 hex chars. Use it as a bearer: ```bash curl -H "Authorization: Bearer rmm_abcdef0123456789..." \ - https://romm.example.com/api/roms + https://demo.romm.app/api/roms ``` Each user gets up to 25 active tokens. Tokens can be paired with a device via the [pairing flow](../ecosystem/client-api-tokens.md), useful when you don't want to type a long token on a handheld. ## OIDC -Users signing in through an OIDC provider get a regular RomM session, same as username/password login. For the API side this means you can't use an OIDC access token directly: authenticate the user through the browser first (they'll be redirected to the OIDC provider, then back to RomM), then use the resulting session cookie, **or** mint a Client API Token for programmatic use. +Users signing in through an OIDC provider get a regular RomM session, same as username/password login. For the API side this means you can't use an OIDC access token directly. Authenticate the user through the browser first (they'll be redirected to the OIDC provider, then back to RomM), then use the resulting session cookie, **or** mint a Client API Token for programmatic use. OIDC provider setup lives in [Administration → OIDC](../administration/oidc/index.md). @@ -121,9 +119,7 @@ Every endpoint in the [API Reference](api-reference.md) lists its required scope - **Read**-ish endpoints want the matching `*.read` scope. - **Write**-ish endpoints want `*.write`. -- **Admin** endpoints (anything under `/api/users` beyond `me`, `/api/tasks/run`) want `users.read`, `users.write`, or `tasks.run`. - -A token that holds `users.write` also implicitly grants lesser scopes like `users.read` or `me.read`, so RomM doesn't require you to list them all. But a token that holds only `roms.read` can't write, even if the underlying user is an Admin. +- **Admin**-ish endpoints want `users.read`, `users.write`, or `tasks.run`. ## Errors @@ -144,7 +140,7 @@ When debugging a 403, check: The full machine-readable schema is served at `/openapi.json`. It's the source of truth for generated clients, Postman collections, and the in-docs [API Reference](api-reference.md). ```bash -curl https://romm.example.com/openapi.json > romm-openapi.json +curl https://demo.romm.app/openapi.json > romm-openapi.json ``` See [Consuming OpenAPI](openapi.md) for codegen tips. diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md index c9275023..5ad563d8 100644 --- a/docs/developers/api-reference.md +++ b/docs/developers/api-reference.md @@ -1,11 +1,11 @@ --- title: API Reference -description: Catalogue of RomM's REST API. Authoritative interactive docs live on each instance. +description: Catalogue of RomM's API. Authoritative interactive docs live on each instance. --- # API Reference -RomM's REST API is documented via **OpenAPI 3.0**. The spec is the source of truth. This page catalogues what's there but every running RomM instance serves its own interactive browser with live "try it out" functionality. +RomM's API is documented via **OpenAPI 3.0**. The spec is the source of truth. This page catalogues what's there but every running RomM instance serves its own interactive browser with live "try it out" functionality. ## Interactive docs diff --git a/docs/developers/index.md b/docs/developers/index.md index 47ef5df9..8ce437ec 100644 --- a/docs/developers/index.md +++ b/docs/developers/index.md @@ -11,7 +11,7 @@ Looking for end-user or operator content? See [Using RomM](../using/index.md) or ## Working with the API -- **[API Reference](api-reference.md):** every REST endpoint. OpenAPI-driven +- **[API Reference](api-reference.md):** every API endpoint. OpenAPI-driven - **[API Authentication](api-authentication.md):** all five auth modes (session, Basic, OAuth2, Client API Token, OIDC) - **[Consuming OpenAPI](openapi.md):** codegen, Postman imports, schema validation - **[WebSockets](websockets.md):** Socket.IO endpoints for live updates and Netplay @@ -42,7 +42,7 @@ Looking for end-user or operator content? See [Using RomM](../using/index.md) or | I want to… | Start here | | -------------------------------- | ------------------------------------------------------------------------------- | -| Call the REST API from a script | [API Authentication](api-authentication.md) + [API Reference](api-reference.md) | +| Call the API from a script | [API Authentication](api-authentication.md) + [API Reference](api-reference.md) | | Generate a client library | [Consuming OpenAPI](openapi.md) | | Sync saves from a handheld | [Device Sync Protocol](../ecosystem/device-sync-protocol.md) | | Listen to scan events | [WebSockets](websockets.md) | diff --git a/docs/developers/openapi.md b/docs/developers/openapi.md index a9ab0d9c..2ff02d03 100644 --- a/docs/developers/openapi.md +++ b/docs/developers/openapi.md @@ -5,7 +5,7 @@ description: Use RomM's OpenAPI spec for code generation, Postman imports, and A # Consuming OpenAPI -RomM ships its entire REST API as an OpenAPI 3.0 specification. That's the source of truth: every endpoint, every request/response schema, every parameter. Use it to generate clients, drive test suites, or import into tools. +RomM ships its entire API as an OpenAPI 3.0 specification. That's the source of truth: every endpoint, every request/response schema, every parameter. Use it to generate clients, drive test suites, or import into tools. ## Where to find the spec @@ -29,7 +29,7 @@ Both are auto-generated from the same `openapi.json`. Swagger UI has a "Try it o The spec is versioned alongside RomM. Every release tag includes a matching `openapi.json` in the release artifacts on GitHub. Snapshot the spec at a specific version if your client needs reproducible builds: ```sh -curl https://romm.example.com/openapi.json > openapi-5.0.0.json +curl https://demo.romm.app/openapi.json > openapi-5.0.0.json ``` ## Rendered API reference @@ -42,7 +42,7 @@ RomM's docs site embeds a rendered version of the spec at [API Reference](api-re ```sh npx @openapitools/openapi-generator-cli generate \ - -i https://romm.example.com/openapi.json \ + -i https://demo.romm.app/openapi.json \ -g python \ -o ./romm-client-python ``` @@ -51,7 +51,7 @@ For TypeScript: ```sh npx @openapitools/openapi-generator-cli generate \ - -i https://romm.example.com/openapi.json \ + -i https://demo.romm.app/openapi.json \ -g typescript-axios \ -o ./romm-client-ts ``` diff --git a/docs/developers/websockets.md b/docs/developers/websockets.md index 42e9deef..0d1dd094 100644 --- a/docs/developers/websockets.md +++ b/docs/developers/websockets.md @@ -27,7 +27,7 @@ If you're writing a non-browser client in a language that has a Socket.IO librar Socket.IO connections inherit the HTTP session: if your `Cookie` header carries a RomM session cookie, the WS connection is authenticated as that user. Programmatic clients should pass the session or token via the handshake `auth` field: ```javascript -const socket = io("https://romm.example.com", { +const socket = io("https://demo.romm.app", { path: "/ws/socket.io", auth: { token: "rmm_...", @@ -126,7 +126,7 @@ def scan_log(data): print(f"[{data['level']}] {data['message']}") sio.connect( - "https://romm.example.com", + "https://demo.romm.app", socketio_path="/ws/socket.io", auth={"token": "rmm_..."}, ) diff --git a/docs/ecosystem/argosy.md b/docs/ecosystem/argosy.md index a8f079f6..4fa7f144 100644 --- a/docs/ecosystem/argosy.md +++ b/docs/ecosystem/argosy.md @@ -34,7 +34,7 @@ Not currently on either, so APK sideloading is the path for now. ## First-time setup 1. Launch Argosy. -2. Enter your RomM URL (e.g. `https://romm.example.com`), which needs HTTPS in production. +2. Enter your RomM URL (e.g. `https://demo.romm.app`), which needs HTTPS in production. 3. Choose **Pair Device**. 4. Argosy shows a pairing URL or QR code: open it on a device that's already signed into RomM. 5. Enter the 8-digit code Argosy displayed on that RomM page. diff --git a/docs/ecosystem/muos-app.md b/docs/ecosystem/muos-app.md index 945e8cad..68955814 100644 --- a/docs/ecosystem/muos-app.md +++ b/docs/ecosystem/muos-app.md @@ -68,7 +68,7 @@ Simplest setup: More-involved setups: -- **Reverse proxy with TLS.** `HOST=https://romm.example.com`. HTTPS works but introduces cert-validation risk on handhelds (some fail strict TLS). +- **Reverse proxy with TLS.** `HOST=https://demo.romm.app`. HTTPS works but introduces cert-validation risk on handhelds (some fail strict TLS). - **Remote access via VPN.** Install Tailscale or similar on the handheld (if supported). This lets the handheld reach RomM from outside the LAN. ## Using the app diff --git a/docs/ecosystem/playnite-plugin.md b/docs/ecosystem/playnite-plugin.md index 72473d6a..526f940e 100644 --- a/docs/ecosystem/playnite-plugin.md +++ b/docs/ecosystem/playnite-plugin.md @@ -50,9 +50,9 @@ Playnite: **Menu → Library → Configure Integrations → RomM**. #### Authentication - **Host URL**: your RomM URL, including scheme, no trailing slash. Examples: - - ✓ `https://romm.example.com` - - ✗ `romm.example.com` - - ✗ `https://romm.example.com/` + - ✓ `https://demo.romm.app` + - ✗ `demo.romm.app` + - ✗ `https://demo.romm.app/` - **Username + password**: Playnite stores these in plaintext. Use a **dedicated Viewer-role account** for Playnite, not your admin account. diff --git a/docs/index.md b/docs/index.md index 1ba74bfc..ca30c880 100644 --- a/docs/index.md +++ b/docs/index.md @@ -59,7 +59,7 @@ RomM (ROM Manager) lets you scan, enrich, organise, and play your game collectio *** - REST API reference, WebSockets, device sync protocol, client tokens. + API reference, WebSockets, device sync protocol, client tokens. [Developers →](developers/index.md) diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md index d81fe584..c117fbc6 100644 --- a/docs/install/kubernetes.md +++ b/docs/install/kubernetes.md @@ -187,7 +187,7 @@ spec: } - { name: ROMM_BASE_URL, - value: "https://romm.example.com", + value: "https://demo.romm.app", } - { name: HASHEOUS_API_ENABLED, value: "true" } # ... other metadata provider vars from the secret @@ -292,10 +292,10 @@ metadata: spec: ingressClassName: nginx tls: - - hosts: [romm.example.com] + - hosts: [demo.romm.app] secretName: romm-tls rules: - - host: romm.example.com + - host: demo.romm.app http: paths: - path: / diff --git a/docs/reference/feeds.md b/docs/reference/feeds.md index 6cb312f4..b2a48f7e 100644 --- a/docs/reference/feeds.md +++ b/docs/reference/feeds.md @@ -51,7 +51,7 @@ Most feeds return JSON: Tinfoil, fpkgi, WebRcade. "success": true, "files": [ { - "url": "https://romm.example.com/api/roms/1234/content/mario.nsp", + "url": "https://demo.romm.app/api/roms/1234/content/mario.nsp", "title": "Super Mario Odyssey", "size": 4500000000, "titleid": "0100000000010000", @@ -66,7 +66,7 @@ Most feeds return JSON: Tinfoil, fpkgi, WebRcade. pkgi uses CSV per upstream's format: one line per game: ```csv -psvita,PCSA00003,Unknown,Game,1.0,https://romm.example.com/...,md5=...,52428800 +psvita,PCSA00003,Unknown,Game,1.0,https://demo.romm.app/...,md5=...,52428800 ``` ### Format per feed diff --git a/docs/reference/ports-and-endpoints.md b/docs/reference/ports-and-endpoints.md index db4ad7f7..5f2eea31 100644 --- a/docs/reference/ports-and-endpoints.md +++ b/docs/reference/ports-and-endpoints.md @@ -124,7 +124,7 @@ environment: nginx inside the container rewrites paths internally, and your reverse proxy forwards the full path. -Not the most common pattern. Most deployments use a subdomain (`romm.example.com`) rather than a path. +Not the most common pattern. Most deployments use a subdomain (`demo.romm.app`) rather than a path. ## See also diff --git a/docs/troubleshooting/authentication.md b/docs/troubleshooting/authentication.md index d66ea1b4..3ede092b 100644 --- a/docs/troubleshooting/authentication.md +++ b/docs/troubleshooting/authentication.md @@ -67,7 +67,7 @@ Check for: - **Trailing slashes**: `/api/oauth/openid` vs `/api/oauth/openid/` are different to the IdP. - **Scheme**: `http://` vs `https://` -- **Host**: `romm.example.com` vs `www.romm.example.com` vs the bare IP +- **Host**: `demo.romm.app` vs `www.demo.romm.app` vs the bare IP - **Port**: implied `80` / `443` on HTTPS vs an explicit port Fix: make them identical on both sides. diff --git a/docs/troubleshooting/sync.md b/docs/troubleshooting/sync.md index ba23855a..cadacbca 100644 --- a/docs/troubleshooting/sync.md +++ b/docs/troubleshooting/sync.md @@ -23,9 +23,9 @@ Troubleshooting paths differ. ### App can't reach RomM -- **URL wrong.** Double-check what you entered in the app config. `https://romm.example.com`, not `https://romm.example.com/api` (the app appends the API path) +- **URL wrong.** Double-check what you entered in the app config. `https://demo.romm.app`, not `https://demo.romm.app/api` (the app appends the API path) - **TLS cert issues.** Self-signed certs cause problems. Use a proper cert (Let's Encrypt through your reverse proxy) or configure the app to skip cert validation (not recommended). -- **Firewall.** From the device, `curl https://romm.example.com/api/heartbeat`. Should return JSON. If not, network path is blocked. +- **Firewall.** From the device, `curl https://demo.romm.app/api/heartbeat`. Should return JSON. If not, network path is blocked. ### Pairing code doesn't work diff --git a/docs/using/console-mode.md b/docs/using/console-mode.md index c6253bd7..86a4c935 100644 --- a/docs/using/console-mode.md +++ b/docs/using/console-mode.md @@ -20,7 +20,7 @@ New in 5.0. If you're on a desktop with a keyboard and mouse, you probably don't Three ways: 1. **Menu bar → Console icon** (looks like a small controller) -2. Type `/console` after your RomM URL: `https://romm.example.com/console`. +2. Type `/console` after your RomM URL: `https://demo.romm.app/console`. 3. Set the default view to Console Mode in **Profile → User Interface → Default view**. This signs you in straight into `/console` every time. Bookmarking `/console` on your TV browser is the most common pattern. diff --git a/docs/using/downloads.md b/docs/using/downloads.md index d95590a8..4913068f 100644 --- a/docs/using/downloads.md +++ b/docs/using/downloads.md @@ -73,7 +73,7 @@ For authenticated programmatic use, a [Client API Token](account-and-profile.md) ```bash curl -H "Authorization: Bearer rmm_..." \ -o mario.sfc \ - https://romm.example.com/api/roms/123/content/mario.sfc + https://demo.romm.app/api/roms/123/content/mario.sfc ``` ## Streaming to an emulator From 346c3bfe4e523380975bd60cadc919b6941276c9 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sat, 25 Apr 2026 18:25:12 -0400 Subject: [PATCH 054/121] trim api-reference to interactive-docs pointer + websockets/versioning Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/developers/api-reference.md | 159 ------------------------------- 1 file changed, 159 deletions(-) diff --git a/docs/developers/api-reference.md b/docs/developers/api-reference.md index 5ad563d8..927ae3ee 100644 --- a/docs/developers/api-reference.md +++ b/docs/developers/api-reference.md @@ -3,10 +3,6 @@ title: API Reference description: Catalogue of RomM's API. Authoritative interactive docs live on each instance. --- -# API Reference - -RomM's API is documented via **OpenAPI 3.0**. The spec is the source of truth. This page catalogues what's there but every running RomM instance serves its own interactive browser with live "try it out" functionality. - ## Interactive docs Every RomM instance hosts two renderings of its own spec: @@ -22,159 +18,6 @@ The raw spec: For code generation, Postman imports, and schema-validation libraries, see [Consuming OpenAPI](openapi.md). -## Base URL - -```text -{romm_url}/api -``` - -## Auth - -Every write endpoint (and most read endpoints) requires a credential. Five modes supported: - -- **Session cookie:** from the web UI -- **HTTP Basic:** username + password header -- **OAuth2 Bearer:** JWT access token -- **Client API Token:** long-lived `rmm_...` bearer token -- **OIDC session:** after IdP callback, same as a regular session - -Full walkthrough in [API Authentication](api-authentication.md). - -## Endpoint groups - -Every endpoint belongs to a group. Summaries below. The interactive docs at `/api/docs` have full request/response schemas. - -### Auth & users - -- `POST /api/auth/login`, `/api/auth/logout`: session login/logout -- `POST /api/token`: OAuth2 token endpoint (password + refresh grants) -- `GET /api/auth/openid`, `/api/oauth/openid`: OIDC flow -- `POST /api/auth/forgot-password`, `/api/auth/reset-password`: password reset -- `GET/POST/PUT/DELETE /api/users`: user management. See [Users & Roles](../administration/users-and-roles.md). -- `POST /api/users/register`: claim an invite link -- `POST /api/users/invite-link`: generate one - -### ROMs - -- `GET /api/roms`: list with filters, pagination, search -- `GET /api/roms/{id}`: single ROM details -- `POST /api/roms/upload/{start,chunk,complete,cancel}`: chunked upload -- `PUT /api/roms/{id}`: update metadata -- `PUT /api/roms/{id}/props`: update per-user data (rating, status) -- `POST /api/roms/delete`: bulk delete -- `GET /api/roms/{id}/content/{filename}`: download -- `GET /api/roms/download?ids=...`: bulk zip download -- `GET /api/roms/by-hash`, `/api/roms/by-metadata-provider`: lookup helpers - -### ROM notes, files, manuals - -- `GET/POST/PUT/DELETE /api/roms/{id}/notes`: per-ROM notes (optional public) -- `GET /api/roms/{id}/files/content/{filename}`: individual file in a multi-file ROM -- `POST/DELETE /api/roms/{id}/manuals`: PDF manual management - -### Platforms - -- `GET /api/platforms`: list -- `GET /api/platforms/supported`: everything RomM recognises (same data as [Supported Platforms](../platforms/supported-platforms.md)) -- `PUT/DELETE /api/platforms/{id}`: edit or delete - -### Collections - -- `GET/POST/PUT/DELETE /api/collections`: standard collections -- `POST/DELETE /api/collections/{id}/roms`: add/remove ROMs -- `GET/POST/PUT/DELETE /api/collections/smart`: [Smart Collections](../using/smart-collections.md) -- `GET /api/collections/virtual`: read-only [Virtual Collections](../using/virtual-collections.md) - -### Assets: saves, states, screenshots - -- `GET/POST/PUT /api/saves`: save files. See [Saves & States](../using/saves-and-states.md). -- `POST /api/saves/delete`: bulk delete -- `GET/POST/PUT /api/states`: emulator states -- `POST /api/screenshots`: screenshot upload - -### Firmware - -- `GET/POST/DELETE /api/firmware`: firmware management. See [Firmware Management](../administration/firmware-management.md). -- `GET /api/firmware/{id}/content/{filename}`: download - -### Devices & sync - -- `GET/POST/PUT/DELETE /api/devices`: registered device management -- `POST /api/sync/negotiate`: sync-session negotiation -- `POST /api/sync/sessions/{id}/complete`: close a sync session with ingested play sessions -- `POST /api/devices/{id}/push-pull`: trigger manual sync - -See [Device Sync Protocol](../ecosystem/device-sync-protocol.md) for the wire-level walkthrough. - -### Play sessions - -- `GET/POST/DELETE /api/play-sessions`: ingest and query play sessions (up to 100 per POST) - -### Client API tokens - -- `GET/POST/DELETE /api/client-tokens`: user's tokens -- `PUT /api/client-tokens/{id}/regenerate`: regenerate the secret -- `POST /api/client-tokens/{id}/pair`: generate a pairing code -- `POST /api/client-tokens/exchange`: exchange a pairing code for a token -- `GET /api/client-tokens/pair/{code}/status`: poll pairing status -- `GET /api/client-tokens/all`, `DELETE /api/client-tokens/{id}/admin`: admin-only - -See [Client API Tokens](../ecosystem/client-api-tokens.md) for the full flow. - -### Search - -- `GET /api/search/roms`: search across metadata providers (IGDB, Moby, SS, Flashpoint, LaunchBox) -- `GET /api/search/cover`: alternate cover search via SteamGridDB - -### Tasks - -- `GET /api/tasks`, `/api/tasks/status`: what's registered, what's running -- `POST /api/tasks/run/{task_name}`: trigger on demand (requires `tasks.run`) - -### Configuration - -- `GET /api/config`: current config (parts of it public, parts require auth) -- `POST/DELETE /api/config/system/platforms`: add/remove platform bindings -- `POST/DELETE /api/config/system/versions`: add/remove version mappings -- `POST/DELETE /api/config/exclude`: add/remove exclusion rules - -### Stats - -- `GET /api/stats`: aggregate counts + disk usage -- `GET /api/stats?include_platform_stats=true`: per-platform breakdown - -### Feeds - -- `GET /api/feeds/webrcade`: WebRcade -- `GET /api/feeds/tinfoil`: Nintendo Switch -- `GET /api/feeds/pkgi/{psvita|psp}/{game|dlc}`: PS Vita / PSP -- `GET /api/feeds/fpkgi/{ps4|ps5}`: PS4 / PS5 -- `GET /api/feeds/kekatsu/{nds}`: Nintendo DS -- `GET /api/feeds/pkgj/{psx|psvita|psp}`: legacy pkgj - -See [Feeds](../reference/feeds.md) for format details per feed. - -### Exports - -- `POST /api/export/gamelist-xml`: ES-DE / Batocera format -- `POST /api/export/pegasus`: Pegasus frontend format - -See [Exports](../reference/exports.md). - -### Heartbeat - -- `GET /api/heartbeat`: health + config snapshot. Safe to scrape from uptime monitors -- `GET /api/heartbeat/metadata/{provider}`: per-provider health - -### Raw - -- `GET /api/raw/assets/{path}`: direct asset passthrough for advanced integrations - -### Netplay - -- `GET /api/netplay/list?game_id=...`: active Netplay rooms for a game -- WebSocket at `/netplay/socket.io`: room coordination. See [WebSockets](websockets.md). - ## WebSockets REST isn't the only surface. Two Socket.IO endpoints cover live-update and coordination use cases: [WebSockets](websockets.md). @@ -187,8 +30,6 @@ RomM's API follows SemVer along with the rest of RomM: - **Minor versions add** endpoints, optional parameters, optional response fields. - **Patch versions fix** bugs without schema changes. -For reproducible builds, pin the OpenAPI spec version at the same RomM version you target. See [Consuming OpenAPI → Versioning](openapi.md#versioning). - ## See also - [API Authentication](api-authentication.md): auth modes in detail From d0f6b8757d3b12be7a16c4233d7aabb4466c454f Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 26 Apr 2026 09:41:48 -0400 Subject: [PATCH 055/121] refresh architecture.md from upstream backend/frontend deep-dives Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/developers/architecture.md | 188 ++++++++++++-------------------- 1 file changed, 72 insertions(+), 116 deletions(-) diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index 9375698b..c2fbdba2 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -3,173 +3,129 @@ title: Architecture description: High-level walkthrough of the RomM codebase: backend, frontend, nginx, workers. --- -# Architecture - -A walkthrough of how RomM is put together, aimed at first-time contributors. Enough to orient yourself before diving in. +A walkthrough of how RomM is put together, aimed at first-time contributors. The authoritative deep-dives live alongside the code at [`docs/BACKEND_ARCHITECTURE.md`](https://github.com/rommapp/romm/blob/main/docs/BACKEND_ARCHITECTURE.md) and [`docs/FRONTEND_ARCHITECTURE.md`](https://github.com/rommapp/romm/blob/main/docs/FRONTEND_ARCHITECTURE.md) in the main repo. ## Repo layout ```text rommapp/romm ├── backend/ # Python FastAPI application -├── frontend/ # Vue 3 + Vuetify SPA (main UI) +│ ├── endpoints/ # Route handlers + Socket.IO under sockets/ +│ ├── handler/ # Business logic: database/, metadata/, filesystem/, auth/ +│ ├── adapters/services/# External API clients (IGDB, Moby, SS, SGDB, RA, ...) +│ ├── models/ # SQLAlchemy ORM +│ ├── tasks/ # RQ jobs: scheduled/ and manual/ +│ ├── alembic/ # 80+ DB migrations +│ └── config/ # Env vars + YAML config manager +├── frontend/ # Vue 3 + Vuetify SPA (main UI + console mode) +│ └── src/ +│ ├── views/ # Page-level components +│ ├── components/ # ~168 components, organised by feature +│ ├── console/ # Console Mode SPA (own router, layout, input bus) +│ ├── stores/ # 18 Pinia stores +│ ├── services/ # Axios API modules + Socket.IO + browser cache +│ └── __generated__/# TS types generated from the backend OpenAPI spec ├── docker/ # nginx config, entrypoint, Dockerfiles -├── examples/ # Reference docker-compose.yml and config.yml -├── env.template # Env var reference -└── pyproject.toml # Backend Python deps +└── examples/ # Reference docker-compose.yml and config.yml ``` -Key sub-directories: - -- `backend/routers/`: FastAPI route definitions, one file per resource group -- `backend/handlers/`: business logic (scan engine, metadata providers, auth) -- `backend/tasks/`: RQ job definitions (scheduled + manual + watcher tasks) -- `backend/config/`: Pydantic config schemas for `config.yml` -- `backend/alembic/`: DB migrations -- `backend/romm_test/`: test suite -- `frontend/src/views/`: top-level Vue pages -- `frontend/src/console/`: Console Mode SPA (separate entry) -- `frontend/src/stores/`: Pinia state stores - ## Runtime topology -A running RomM container hosts several cooperating processes: - ```ascii ┌─────────────────────────────────────────────────────────┐ │ docker container │ │ │ -│ ┌───────┐ HTTP ┌───────┐ python ┌───────────┐ │ -│ │ nginx │────────→│ gunicorn│──→│ FastAPI │ │ -│ │ :8080 │ │ :5000 │ │ backend │ │ -│ └───┬───┘ └─────────┘ └────┬────┘ │ -│ │ static files SQL │ │ -│ │ (EmulatorJS, │ │ -│ │ Ruffle, SPA) ↓ │ -│ │ ┌─────────┐ │ -│ │ │ MariaDB │(external) │ -│ ↓ │ (replica)│ │ -│ ┌──────┐ └─────────┘ │ -│ │ serve│ │ -│ │ /library│←── bind mount from host │ -│ │ /assets │ read/write │ -│ │ /resources│ │ -│ └──────┘ │ +│ ┌───────┐ HTTP ┌──────────┐ python ┌──────────┐ │ +│ │ nginx │─────────→│ gunicorn │─────────→│ FastAPI │ │ +│ │ :8080 │ │ :5000 │ │ backend │──┐ +│ └───┬───┘ └──────────┘ └──────────┘ │ SQL +│ │ static files (SPA, EmulatorJS, Ruffle) ↓ +│ │ X-Accel-Redirect for downloads ┌──────────┐ +│ │ │ MariaDB │ +│ ↓ │ (or PG / │ +│ /library /assets /resources │ MySQL) │ +│ (host bind mounts) └──────────┘ │ │ -│ ┌────────┐ ┌────────┐ │ -│ │ RQ │←──────────→│ Valkey │ │ -│ │ workers│ tasks │ (embedded or external) │ -│ └────────┘ └────────┘ │ +│ ┌──────────┐ ┌──────────────────────┐ │ +│ │ RQ │←───────→│ Valkey │ │ +│ │ workers │ jobs │ (embedded or external)│ │ +│ └──────────┘ └──────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` -### The parts - -- **nginx:** listens on `8080`. Serves static assets (the SPA bundle, EmulatorJS core files, Ruffle, cover images). Proxies API + WebSocket traffic to gunicorn. Streams large downloads via `mod_zip`. -- **gunicorn:** the Python WSGI server, running the FastAPI app. Multiple worker processes. `WEB_SERVER_CONCURRENCY` tunes count. -- **FastAPI backend:** routes, handlers, DB access via SQLAlchemy, Valkey for sessions + cache + queue -- **RQ workers:** separate process(es) that pop jobs off Valkey queues and run them. Scans, metadata syncs, cleanup tasks -- **Valkey:** in-container by default (full image), externalisable (Redis-compatible) -- **MariaDB / Postgres / MySQL / SQLite:** always external (or a separate container) +- **nginx** serves the SPA bundle, EmulatorJS, Ruffle, and cover art. Proxies API + WebSocket traffic to gunicorn. Streams downloads via `mod_zip` and `X-Accel-Redirect`. +- **gunicorn** runs the FastAPI app under multiple workers (`WEB_SERVER_CONCURRENCY`). +- **FastAPI backend** on Python 3.13+, SQLAlchemy 2.0, `python-socketio`. Talks to the DB, the Valkey cache, and 10+ external metadata providers. +- **RQ workers** pop jobs off three priority queues (`high_prio_queue`, `default_queue`, `low_prio_queue`) backed by Valkey. +- **Valkey** (Redis-compatible) is in-container by default, externalisable. See [Embedded vs External Valkey](../install/redis-or-valkey.md). +- **Database** is always external. MariaDB 10.5+ default. MySQL 8.0+ and PostgreSQL also supported. +- **Filesystem watcher** (optional) enqueues rescans on library changes via `watchfiles`. ## Request lifecycle -### Browsing the library (GET `/api/roms`) - -1. Browser sends request → reverse proxy → nginx. -2. nginx forwards to gunicorn → FastAPI → `backend/routers/roms.py`. -3. Handler queries MariaDB via SQLAlchemy. -4. Response serialised, returned. - -Simple. Same flow for every non-WebSocket API call. +The middleware stack runs in front of every route — CORS → CSRF → authentication → Valkey-backed session → context vars (aiohttp + httpx clients) — then FastAPI dispatches to an endpoint that calls into a handler. -### Uploading a ROM (POST `/api/roms/upload/*`) +A few flows worth knowing: -1. Browser → nginx. nginx buffers up to `client_max_body_size`. -2. gunicorn → FastAPI → `backend/routers/roms_upload.py`. -3. Handler writes chunks to a temp path, assembles on complete. -4. Moved into place under `/romm/library`. -5. DB entry created. -6. Socket.IO emits `rom:created`. +- **Reads** (`GET /api/roms`) hit `endpoints/roms/__init__.py`, which calls `db_roms_handler` for the SQLAlchemy query and returns a Pydantic schema. +- **Uploads** (`POST /api/roms/upload/*`) are chunked. `init` returns an upload session ID held in Valkey for 24 h. `chunk` pushes up to 64 MB at a time. `complete` assembles, hashes, moves under `/romm/library`, writes the DB row, and emits `rom:created` over Socket.IO. +- **Scans** are enqueued as RQ jobs. The worker walks platform folders, parses filenames, hashes files, queries metadata providers in priority order (IGDB → Moby → SS → LaunchBox → Hasheous → RA → Flashpoint → HLTB → TGDB), downloads artwork, and upserts. Progress streams over Socket.IO. Six modes — `NEW_PLATFORMS`, `QUICK`, `UPDATE`, `UNMATCHED`, `COMPLETE`, `HASHES`. -Large uploads go through the chunked-upload flow for a reason: gunicorn's per-request memory ceiling would otherwise be an issue. +## Backend -### Scanning +**Layers.** Endpoints validate and serialise. Handlers hold the business logic, split by concern under `handler/database/`, `handler/metadata/`, `handler/filesystem/`, and `handler/auth/`. Models are SQLAlchemy. Adapters wrap external APIs. Roughly 175 HTTP routes and 11 Socket.IO handlers across 24 routers. -1. Trigger: UI button, `/api/tasks/run/scan`, scheduled cron, or watcher event. -2. Handler enqueues an RQ job. -3. Worker pops job, runs `handlers/scan.py`: - - Walks filesystem. - - Hashes files. - - Calls metadata providers in parallel. - - Writes to DB. -4. Emits `scan:progress` and `scan:log` via Socket.IO during execution. -5. Emits `scan:complete` when done. +**Database.** `roms` is the central entity, linking to `platforms`, `rom_files`, `roms_metadata` (aggregated provider data), `rom_user` (per-user tracking), `rom_notes`, and `sibling_roms` (M:M self-ref for alternate versions). Saves, states, and screenshots FK to both `roms` and `users`. Three collection flavours — manual `collections`, dynamic `smart_collections` (filter-criteria with cached IDs), and read-only `virtual_collections` (a database view, excluded from migrations). -## Frontend architecture +**Auth.** `HybridAuthBackend` walks the methods in order — session cookie, HTTP Basic, OAuth2 Bearer JWT, Client API Token (`rmm_...`, SHA-256 lookup), OIDC, kiosk mode. Token plaintext is never stored. See [API Authentication](api-authentication.md). -### Main UI (`frontend/src/`) +**Metadata.** Each provider has a handler under `handler/metadata/` that normalises responses into a common shape. Priority is configurable in `config.yml`. Static fixtures (MAME, ScummVM, PS1/PS2/PSP serial maps, known BIOS hashes) load into Valkey at startup. Hashing is platform-aware — CHD v5, PICO-8, and the RetroAchievements per-platform algorithm all get special handling. Switch and PS3/4/5 skip hashing. -- **Vue 3 + Composition API** -- **Vuetify 3** for UI components -- **Vite** for build + dev server with HMR -- **Pinia** for state management: one store per resource family (auth, roms, platforms, collections, etc.) -- **vue-i18n** for localisation. Translations in `src/locales//` -- **socket.io-client** for live updates +**Configuration.** Env vars (100+, see `env.template`) for infrastructure, plus YAML (`config.yml`) for library/scan/emulator behaviour. `ConfigManager` is a singleton. See [Configuration File](../reference/configuration-file.md). -Router in `src/plugins/router.ts`. Top-level views in `src/views/`. +## Frontend -### Console Mode SPA (`frontend/src/console/`) +Vue 3 + Composition API + TypeScript, built with Vite. UI is Vuetify 3 plus Tailwind CSS 4. State lives in 18 Pinia stores (auth, ROMs, platforms, collections, gallery filters, scan progress, etc.). Vue Router covers 36 named routes across three layouts — Auth (public), Main (authenticated), and Console (gamepad/TV). Translations via vue-i18n in 17 locales. Mitt is used as a loose event bus to trigger dialogs from anywhere. -Separate SPA, compiled as a second bundle. Entry point at `/console`. Built with the same Vue + Pinia + i18n stack but: +TypeScript types in `src/__generated__/` are generated from the backend OpenAPI spec via `npm run generate`, giving type-safe end-to-end communication. -- Own router with `/console` namespace -- Own component library tuned for gamepad navigation (bigger targets, spatial focus, SFX) -- Own theme in `src/console/styles/` +The Axios instance carries a 2-minute timeout, injects the CSRF token from the `romm_csrftoken` cookie, and on 403 clears the session and redirects to `/login`. UI preferences persist to localStorage and sync to `user.ui_settings` on the backend. -## Background work +**Console Mode** is a second SPA bundle for TV and gamepad. Stack-based input bus, grid-based spatial navigation, gamepad polling in `requestAnimationFrame`, and SFX synthesised via the Web Audio API. -RQ has three priority queues: +## Background jobs -- **high:** user-triggered scans, manual tasks. Get fast -- **default:** scheduled nightlies, sync operations -- **low:** cleanup, image conversion. Run when the system's idle. +RQ workers run scheduled jobs (rescans, Switch TitleDB refresh, LaunchBox refresh, image-to-WebP conversion, RA progress sync, netplay cleanup) and manual tasks (`cleanup_missing_roms`, `cleanup_orphaned_resources`, `sync_folder_scan`). Each scheduled task is gated by an `ENABLE_SCHEDULED_*` env var. -All queues share the same worker pool. Jobs are persisted to Valkey, so restarting RomM doesn't lose in-flight work (appendonly needs to be on, see [Redis or Valkey](../install/redis-or-valkey.md)). +Jobs persist to Valkey, so restarts don't lose in-flight work — but only if `appendonly` is on. -## Auth +## Real-time -Session state in Valkey. Passwords bcrypt-hashed in the DB. JWT for OAuth2 bearer tokens. +Two Socket.IO servers, both Valkey-backed for horizontal scaling — `/ws` for scan progress and notifications, `/netplay` for netplay rooms. See [WebSockets](websockets.md). -OIDC handled via `authlib` on the backend. Session cookies issued after successful OIDC callback. From there, it's a regular session. +## Filesystem layout -Client API Tokens are stored as hash-only in the DB (we never store the plaintext token after creation). A token is validated by hashing the presented bearer and comparing. +```text +{ROMM_BASE_PATH}/ # Default: /romm +├── library/{platform_slug}/ # ROMs (roms/) and BIOS (bios/) +├── resources/roms/{rom_id}/ # Cached cover art + screenshots +├── assets/users/{user_id}/ # User saves, states, screenshots +└── config/config.yml # YAML configuration +``` ## Observability -- **Sentry** (opt-in): unhandled exceptions -- **OpenTelemetry** (opt-in): traces, metrics, logs via OTLP -- **`/api/heartbeat`:** aggregated health snapshot - -See [Observability](../administration/observability.md). - -## Why these choices - -- **FastAPI**: modern, async, auto-OpenAPI. Good fit for the API-shape of the app -- **Vue + Vuetify**: fast, componenty, looks decent out of the box -- **MariaDB** as default: solid, familiar, works everywhere -- **RQ** not Celery: simpler, Redis-native, less operational overhead. Works fine at RomM's scale -- **Socket.IO**: pragmatic. Raw WebSocket would be leaner but SIO's client ecosystem is worth the overhead. +Sentry (opt-in via `SENTRY_DSN`) captures unhandled exceptions. OpenTelemetry (opt-in) ships traces, metrics, and logs over OTLP. `GET /api/heartbeat` returns an aggregated health snapshot, safe to scrape from uptime monitors. See [Observability](../administration/observability.md). ## Contributing -See [Contributing](contributing.md) for the process + style expectations. - -For large changes, read the relevant handler in `backend/handlers/` first. The patterns there will guide what you write. +See [Contributing](contributing.md) for process and style. For non-trivial backend changes, read the relevant handler in `backend/handler/` first. ## See also - [Development Setup](development-setup.md): get a local env running - [API Reference](api-reference.md): what the backend exposes -- [WebSockets](websockets.md): Socket.IO endpoints in detail +- [API Authentication](api-authentication.md): auth modes in detail +- [WebSockets](websockets.md): Socket.IO endpoints - [Configuration File](../reference/configuration-file.md): `config.yml` schema +- [Embedded vs External Valkey](../install/redis-or-valkey.md): cache + queue store From 5c14f81c25befa6412455006545da8e8d78bd59f Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 26 Apr 2026 14:23:12 -0400 Subject: [PATCH 056/121] rewrite developer docs to match administration tone Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/developers/architecture.md | 111 +++++++++++++++++---------- docs/developers/contributing.md | 62 +++++++-------- docs/developers/development-setup.md | 86 +++++++++++---------- docs/developers/i18n.md | 72 ++++++++--------- docs/developers/index.md | 46 +++++------ docs/developers/openapi.md | 74 ++++++++---------- docs/developers/releasing.md | 94 ++++++++++------------- docs/developers/websockets.md | 102 ++++++++++++------------ 8 files changed, 322 insertions(+), 325 deletions(-) diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index c2fbdba2..f0678b0d 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -1,9 +1,11 @@ --- title: Architecture -description: High-level walkthrough of the RomM codebase: backend, frontend, nginx, workers. +description: High-level walkthrough of the RomM codebase --- -A walkthrough of how RomM is put together, aimed at first-time contributors. The authoritative deep-dives live alongside the code at [`docs/BACKEND_ARCHITECTURE.md`](https://github.com/rommapp/romm/blob/main/docs/BACKEND_ARCHITECTURE.md) and [`docs/FRONTEND_ARCHITECTURE.md`](https://github.com/rommapp/romm/blob/main/docs/FRONTEND_ARCHITECTURE.md) in the main repo. +# Architecture + +What you need to know to find your way around `rommapp/romm` before you start changing things. The exhaustive deep-dives live alongside the code at [`docs/BACKEND_ARCHITECTURE.md`](https://github.com/rommapp/romm/blob/main/docs/BACKEND_ARCHITECTURE.md) and [`docs/FRONTEND_ARCHITECTURE.md`](https://github.com/rommapp/romm/blob/main/docs/FRONTEND_ARCHITECTURE.md). This page is the orientation pass. ## Repo layout @@ -16,7 +18,7 @@ rommapp/romm │ ├── models/ # SQLAlchemy ORM │ ├── tasks/ # RQ jobs: scheduled/ and manual/ │ ├── alembic/ # 80+ DB migrations -│ └── config/ # Env vars + YAML config manager +│ └── config/ # Env vars + YAML config.json manager ├── frontend/ # Vue 3 + Vuetify SPA (main UI + console mode) │ └── src/ │ ├── views/ # Page-level components @@ -31,6 +33,8 @@ rommapp/romm ## Runtime topology +A running RomM container hosts several cooperating processes: + ```ascii ┌─────────────────────────────────────────────────────────┐ │ docker container │ @@ -40,7 +44,7 @@ rommapp/romm │ │ :8080 │ │ :5000 │ │ backend │──┐ │ └───┬───┘ └──────────┘ └──────────┘ │ SQL │ │ static files (SPA, EmulatorJS, Ruffle) ↓ -│ │ X-Accel-Redirect for downloads ┌──────────┐ +│ │ ┌──────────┐ │ │ │ MariaDB │ │ ↓ │ (or PG / │ │ /library /assets /resources │ MySQL) │ @@ -53,55 +57,85 @@ rommapp/romm └─────────────────────────────────────────────────────────┘ ``` -- **nginx** serves the SPA bundle, EmulatorJS, Ruffle, and cover art. Proxies API + WebSocket traffic to gunicorn. Streams downloads via `mod_zip` and `X-Accel-Redirect`. -- **gunicorn** runs the FastAPI app under multiple workers (`WEB_SERVER_CONCURRENCY`). -- **FastAPI backend** on Python 3.13+, SQLAlchemy 2.0, `python-socketio`. Talks to the DB, the Valkey cache, and 10+ external metadata providers. -- **RQ workers** pop jobs off three priority queues (`high_prio_queue`, `default_queue`, `low_prio_queue`) backed by Valkey. -- **Valkey** (Redis-compatible) is in-container by default, externalisable. See [Embedded vs External Valkey](../install/redis-or-valkey.md). -- **Database** is always external. MariaDB 10.5+ default. MySQL 8.0+ and PostgreSQL also supported. -- **Filesystem watcher** (optional) enqueues rescans on library changes via `watchfiles`. - ## Request lifecycle -The middleware stack runs in front of every route — CORS → CSRF → authentication → Valkey-backed session → context vars (aiohttp + httpx clients) — then FastAPI dispatches to an endpoint that calls into a handler. +Every request runs the middleware stack in order, CORS → CSRF → authentication → Valkey-backed session → context vars (aiohttp + httpx clients), before FastAPI dispatches to the endpoint. Handlers do the actual work and Pydantic schemas serialise the response. -A few flows worth knowing: +Three flows are worth knowing: -- **Reads** (`GET /api/roms`) hit `endpoints/roms/__init__.py`, which calls `db_roms_handler` for the SQLAlchemy query and returns a Pydantic schema. -- **Uploads** (`POST /api/roms/upload/*`) are chunked. `init` returns an upload session ID held in Valkey for 24 h. `chunk` pushes up to 64 MB at a time. `complete` assembles, hashes, moves under `/romm/library`, writes the DB row, and emits `rom:created` over Socket.IO. -- **Scans** are enqueued as RQ jobs. The worker walks platform folders, parses filenames, hashes files, queries metadata providers in priority order (IGDB → Moby → SS → LaunchBox → Hasheous → RA → Flashpoint → HLTB → TGDB), downloads artwork, and upserts. Progress streams over Socket.IO. Six modes — `NEW_PLATFORMS`, `QUICK`, `UPDATE`, `UNMATCHED`, `COMPLETE`, `HASHES`. +- **Reads** (`GET /api/roms`) are the simple case: endpoint → `db_roms_handler` → SQLAlchemy → response schema. +- **Uploads** (`POST /api/roms/upload/*`) are chunked, because gunicorn's per-request memory ceiling makes single-shot multi-GB uploads unworkable. `init` allocates an upload session held in Valkey for 24 h, `chunk` pushes up to 64 MB per call, and `complete` assembles, hashes, moves the file under `/romm/library`, writes the DB row, and emits `rom:created` over Socket.IO. +- **Scans** are enqueued as RQ jobs. The worker walks platform folders, parses filenames, hashes files, queries metadata providers in priority order, downloads artwork, and upserts. Progress streams over Socket.IO. Six modes — `NEW_PLATFORMS`, `QUICK`, `UPDATE`, `UNMATCHED`, `COMPLETE`, `HASHES`. Operator-side detail in [Scanning & Watcher](../administration/scanning-and-watcher.md). ## Backend -**Layers.** Endpoints validate and serialise. Handlers hold the business logic, split by concern under `handler/database/`, `handler/metadata/`, `handler/filesystem/`, and `handler/auth/`. Models are SQLAlchemy. Adapters wrap external APIs. Roughly 175 HTTP routes and 11 Socket.IO handlers across 24 routers. +### Layers + +The backend follows a fairly conventional layering. Endpoints handle request validation and response serialisation, while the actual business logic lives in handlers organised by concern: `handler/database/` for per-entity CRUD, `handler/metadata/` for provider-specific normalisation, `handler/filesystem/` for I/O, and `handler/auth/` for the multi-method auth backend. Models are SQLAlchemy ORM, and adapters wrap the external APIs. All told, the codebase exposes around 175 HTTP routes and 11 Socket.IO handlers across 24 routers. + +### Database + +`roms` is the central entity, linking to `platforms` (M:1), `rom_files` (1:M), `roms_metadata` (1:1, aggregated provider data), `rom_user` (per-user, per-ROM tracking), `rom_notes`, and `sibling_roms` (self-ref M:M for alternate versions). Saves, states, and screenshots FK to both `roms` and `users`. `device_save_sync` tracks per-device sync state. + +Three collection flavours coexist — manually curated `collections` (M:M to ROMs), dynamic `smart_collections` with cached IDs, and read-only `virtual_collections` (a database view excluded from migrations). User-side: [Smart Collections](../using/smart-collections.md), [Virtual Collections](../using/virtual-collections.md). -**Database.** `roms` is the central entity, linking to `platforms`, `rom_files`, `roms_metadata` (aggregated provider data), `rom_user` (per-user tracking), `rom_notes`, and `sibling_roms` (M:M self-ref for alternate versions). Saves, states, and screenshots FK to both `roms` and `users`. Three collection flavours — manual `collections`, dynamic `smart_collections` (filter-criteria with cached IDs), and read-only `virtual_collections` (a database view, excluded from migrations). +Migrations live in `backend/alembic/versions/` and run on every container start. They support SQLite batch mode plus DB-specific SQL where MariaDB, MySQL, and PostgreSQL diverge. -**Auth.** `HybridAuthBackend` walks the methods in order — session cookie, HTTP Basic, OAuth2 Bearer JWT, Client API Token (`rmm_...`, SHA-256 lookup), OIDC, kiosk mode. Token plaintext is never stored. See [API Authentication](api-authentication.md). +### Authentication -**Metadata.** Each provider has a handler under `handler/metadata/` that normalises responses into a common shape. Priority is configurable in `config.yml`. Static fixtures (MAME, ScummVM, PS1/PS2/PSP serial maps, known BIOS hashes) load into Valkey at startup. Hashing is platform-aware — CHD v5, PICO-8, and the RetroAchievements per-platform algorithm all get special handling. Switch and PS3/4/5 skip hashing. +`HybridAuthBackend` walks methods in order — session cookie (looked up in Valkey), HTTP Basic (bcrypt), OAuth2 Bearer JWT (HS256), Client API Token (`rmm_...`, SHA-256 lookup), OIDC, kiosk mode if enabled. Token plaintext is never stored: we hash on creation and compare hashes on every request. Full client-side reference: [API Authentication](api-authentication.md). -**Configuration.** Env vars (100+, see `env.template`) for infrastructure, plus YAML (`config.yml`) for library/scan/emulator behaviour. `ConfigManager` is a singleton. See [Configuration File](../reference/configuration-file.md). +### Metadata providers + +Each provider has a handler under `handler/metadata/` that normalises responses into a common shape. Priority is configurable in `config.yml` (`scan.priority.metadata`). First match wins per field, with manual overrides on top. + +Static fixtures (MAME, ScummVM, PS1/PS2/PSP serial maps, known BIOS hashes) load into Valkey at startup so lookups don't hit disk. + +Hashing is platform-aware: CHD v5 pulls its SHA1 straight from the file header, PICO-8 cartridges (`.p8.png`) get a special-cased extractor, and the RetroAchievements per-platform algorithm runs through `rahasher.py`. Switch and PS3/4/5 skip hashing entirely, since those ROMs aren't reasonably hashable in the first place. + +### Configuration + +Configuration sits at two layers — env vars (100+ of them, all listed in `env.template`) for infrastructure concerns, and YAML (`config.yml`) for everything to do with the library, scanning, and emulator behaviour. The YAML side is read, validated, and written back through the singleton `ConfigManager`. The schema reference lives in [Configuration File](../reference/configuration-file.md). ## Frontend -Vue 3 + Composition API + TypeScript, built with Vite. UI is Vuetify 3 plus Tailwind CSS 4. State lives in 18 Pinia stores (auth, ROMs, platforms, collections, gallery filters, scan progress, etc.). Vue Router covers 36 named routes across three layouts — Auth (public), Main (authenticated), and Console (gamepad/TV). Translations via vue-i18n in 17 locales. Mitt is used as a loose event bus to trigger dialogs from anywhere. +### Stack -TypeScript types in `src/__generated__/` are generated from the backend OpenAPI spec via `npm run generate`, giving type-safe end-to-end communication. +The frontend is a Vue 3 SPA written in TypeScript (with `noImplicitAny`) using the Composition API and `