Skip to content

Repository files navigation

RAChecker Docker

Docker and Docker Compose deployment assets for RAChecker, the upstream RetroAchievements ROM scanner. This is an independent packaging repository, not the RAChecker source project. The image fetches the selected upstream revision during its build. This repository contains no RAChecker checkout, ROM library, credentials, or published image.

Requirements

  • Docker Engine with Docker Compose v2
  • An x86-64 Linux Docker host for the bundled RAHasher executable
  • Network access during image build to fetch the selected upstream revision
  • An operator-controlled ROM directory only when optional ROM access is enabled

The service listens on container port 8088. The default host binding is 127.0.0.1:8088; change it explicitly when wider access is required.

Quick start with Docker Compose

Clone this repository, create the local environment file, and set the host ROM directory you want RAChecker to scan:

cp .env.example .env

At minimum, keep the supported RACHECKER_REF=v0.17.0 value and set ROM_LIBRARY_PATH to an absolute host path. Add RA_USERNAME and RA_API_KEY if authenticated RetroAchievements operations are required. Then resolve and start the development deployment with the optional read-only ROM mount:

docker compose -f compose.development.yml -f compose.roms.yml config
docker compose -f compose.development.yml -f compose.roms.yml up -d --build
docker compose -f compose.development.yml -f compose.roms.yml ps
docker compose -f compose.development.yml -f compose.roms.yml logs -f rachecker

Open http://127.0.0.1:8088. Stop the deployment without deleting persisted configuration:

docker compose -f compose.development.yml -f compose.roms.yml down

The development deployment persists RAChecker state under ./config. The ROM directory is mounted read-only at /roms. Do not use down -v when operating the production deployment if its named configuration volume must be retained.

Configuration

Create a local environment file from the non-secret template:

cp .env.example .env

Set RACHECKER_REF to the supported v0.17.0 tag or its matching commit daa29b232313760353b857840808e920e9f77977. Floating branches, latest, and other revisions are not supported. Optional runtime settings are:

  • RA_USERNAME and RA_API_KEY for RetroAchievements access
  • FILE__RA_API_KEY to load RA_API_KEY from a readable runtime secret file
  • PUID, PGID, TZ, and UMASK for identity and process defaults
  • ROM_LIBRARY_PATH only when an optional ROM mount is explicitly enabled

Credentials and host-specific paths belong in the ignored .env file or an external secret mechanism. They are not image build arguments, labels, or committed values. The default Compose configuration has no ROM mount.

Build and operate

Build from the repository root with the selected immutable revision:

docker build --build-arg RACHECKER_REF=v0.17.0 -t rachecker-docker:v0.17.0 .

The build records the resolved upstream commit in image metadata and fails if the revision cannot be fetched or the application cannot be built. It also installs the checksum-pinned official RAHasher 1.8.4 Linux x86-64 executable behind a compatibility wrapper at /usr/local/bin/RAHasher and sets RA_RAHASHER to that path. Archive extraction tooling remains confined to a build stage and is absent from the final image.

Linux RAHasher compatibility fix

The official Linux RAHasher can print a valid hash and still exit with status 1. RAChecker interprets that non-zero status as a hashing error and discards the otherwise valid result. The bundled wrapper executes the untouched official binary, passes through its stdout and stderr, and changes the exit status to 0 only when stdout contains a standalone 32-character hexadecimal hash. If no valid hash appears, the wrapper preserves the real exit status and diagnostics. Signals are forwarded and the temporary stdout buffer is removed on success, failure, or interruption. The fix is limited to the observed process-contract mismatch; it does not manufacture hashes or hide genuine RAHasher failures.

Validate and start the default development stack:

docker compose config
docker compose up -d --build

The default compose.yml imports the development service model. Select the production model explicitly:

docker compose -f compose.production.yml config
docker compose -f compose.production.yml up -d --build

Inspect state and diagnostics with the same Compose project and file:

docker compose ps
docker compose logs -f rachecker

The production health check is the availability signal. If HTTP availability is not reached within the health-check window, the scoped failure is runtime.unhealthy: the service remains unhealthy, with state and logs available. The service remains unhealthy with logs available, and no healthy result should be assumed. Inspect the health output and service logs before remediation. A healthy service is available at http://127.0.0.1:8088 with the default host binding.

Stop without deleting persistent state:

docker compose stop
docker compose down

After changing configuration or RACHECKER_REF, rebuild and recreate:

docker compose up -d --build --force-recreate

Keep the build output, health state, and service logs when diagnosing failed fetch, build, startup, health, or shutdown operations.

Persistence and ROM access

/config is the stable persistence boundary for mutable RAChecker configuration, SQLite data, metadata, and hash caches. Development Compose uses explicit repository-local bind directories. Production Compose uses explicit Compose-managed named volumes. Both are outside the image layer and survive ordinary container recreation and image rebuilds.

In production, a bounded one-shot initializer applies the configured non-root PUID and PGID to the named /config volume, defaulting to 1000:1000. The long-running service then starts directly as that numeric non-root identity. Changing PUID or PGID changes the effective writer identity. Initialization changes only the declared writable /config path and receives only the CHOWN capability required for that ownership handoff. The named /config volume is written by the container's configured non-root application identity after initialization. The volume is not a promise that arbitrary host users can read or write its contents.

Production /config ownership is the configured non-root PUID/PGID writer.

The selected Compose file is authoritative for the actual volume name and service wiring. Do not delete development directories or production volumes when their state is needed. docker compose down -v, docker volume rm, and broad docker volume prune can destroy RAChecker state and require deliberate operator confirmation.

/roms is an optional external ROM library. It is never copied into the image or /config, and any configured bind mount is read-only. The host source must be readable by the configured non-root identity; initialization does not change its ownership or permissions. Enabling the mount without ROM_LIBRARY_PATH fails Compose interpolation before container creation. An unreadable mount fails visibly as runtime.roms_unreadable. Startup or scanning fails visibly; permissions are not broadened automatically. Correct host permissions or choose compatible PUID and PGID values.

The image includes Linux RAHasher for supported disc-image identification. RAHasher is executed from the read-only image and reads the operator-mounted ROM library without changing it. The packaged official artifact is x86-64, so other container architectures fail the image build instead of installing an incompatible executable.

Enable the read-only ROM mount explicitly by layering the override onto either Compose model:

ROM_LIBRARY_PATH=path/to/roms docker compose -f compose.development.yml -f compose.roms.yml up -d --build
ROM_LIBRARY_PATH=path/to/roms docker compose -f compose.production.yml -f compose.roms.yml up -d --build

The override fails Compose interpolation when ROM_LIBRARY_PATH is missing. The default and production models do not mount /roms until the override is selected.

Persistent storage is not backup support. Automated backup is unsupported, and automated restore is unsupported. This repository supplies no retention, snapshot, or migration procedure and makes no claim that an external copy is restorable. If an operator uses external storage tooling, stop the service and wait for quiescence first, preserve the original, and validate the restored application independently. Never copy live SQLite or cache files, restore over the active volume, or delete the original as part of an unverified operation.

Upgrades

Containers do not update upstream source automatically. To upgrade, update the supported pinned release and its resolved commit in the image contract, select that release through RACHECKER_REF, then rebuild and recreate the service. Keep the existing /config storage. The selected upstream revision defines state-format compatibility and any required migration; this repository does not migrate or rewrite application state.

If the new revision is unhealthy or cannot read existing state, preserve the storage, inspect docker compose ps and docker compose logs rachecker, and resolve the issue before any destructive storage operation. The container ROM path remains /roms regardless of the host source.

Mismatch report utility

scripts/rachecker-mismatch-report.py is a dependency-free Python 3 CLI for reviewing RAChecker records whose status is no_match. It never reports records already marked match or error, but reads matched game IDs so a no-match duplicate is suppressed when the collection already contains a supported ROM for the same RA game. For each no-match file it searches RAChecker's local game API, requires the same console and an achievement-bearing game, rejects weak or ambiguous title guesses, and retrieves RAChecker's authoritative supported ROM names, MD5 hashes, labels, and patch URLs.

Run it on a host that can reach RAChecker and can see the same ROM tree. The --rachecker-rom-mapping value is the local directory corresponding to the container's /roms; --roms may be that directory or one of its subdirectories:

python3 scripts/rachecker-mismatch-report.py \
  --rachecker http://127.0.0.1:8088 \
  --roms /srv/roms \
  --rachecker-rom-mapping /srv/roms

Human-readable text is the default. It is sorted by console, lists each local incompatible ROM, identifies the likely RA game, and shows every exact supported ROM filename and MD5. Patch links are included when RAChecker provides them. Colour is automatic for terminals and can be controlled with --color auto, --color always, or --color never.

Write machine-readable JSON or CSV:

python3 scripts/rachecker-mismatch-report.py \
  --rachecker http://127.0.0.1:8088 \
  --roms /srv/roms \
  --rachecker-rom-mapping /srv/roms \
  --format json --output mismatch-report.json

Print aggregate accepted/fixable, low/uncertain, patch-version mismatch, likely region mismatch, and unsupported-version-or-bad-dump counts:

python3 scripts/rachecker-mismatch-report.py \
  --rachecker http://127.0.0.1:8088 \
  --roms /srv/roms \
  --rachecker-rom-mapping /srv/roms \
  --summary

Use --include-rejected to audit weak and ambiguous guesses with rejection reasons. By default the utility refuses to produce a partial report while a scan is active; --allow-active-scan explicitly permits that incomplete view. Candidate titles must also meet --minimum-title-similarity, a normalized edit similarity from 0 to 1 that defaults to 0.90. Exact RA title words contained at a token boundary in a longer local title are accepted so meaningful version or variant suffixes remain eligible. Summary and structured output count already-supported duplicates separately from uncertain rejections. Files detected as hacks, including files beneath a Hacks directory, can only match RA titles explicitly marked as a hack, bootleg, or homebrew. This prevents modified ROM names from being reported as fixes for the unmodified base game. Filename matching is heuristic. A candidate means an exact supported RA ROM exists for the likely game, not that the local file's identity was proven from its filename. Each accepted candidate is classified as a patch/version mismatch when a detected modified ROM has a supported patch, a likely region mismatch when supported RA filenames identify one or more regions, or an unsupported version or bad dump otherwise. This unsupported version or bad dump diagnosis is the fallback. Region diagnosis is intentionally heuristic: an unsupported local hash may instead be another revision or dump variation. Text, JSON, and CSV include the likely issue and supported regions.

Use --patch-candidate-mirror to optionally build a local mirror while producing any text, JSON, CSV, or summary report:

python3 scripts/rachecker-mismatch-report.py \
  --rachecker http://127.0.0.1:8088 \
  --roms /srv/roms \
  --rachecker-rom-mapping /srv/roms \
  --patch-candidate-mirror /srv/ra-patches

Only accepted candidates with patch links enter the mirror. Each incompatible ROM is copied, never symlinked, beneath a normalized <console>/<RA game title>/ directory, alongside all advertised patches under their original decoded filenames. Slashes in directory names become -.

ROM copies and patches are append-only by filename. Existing regular files are retained without opening, hashing, comparing, downloading, or replacing them; new filenames are copied or downloaded atomically. Delete an existing mirror file manually if a same-name ROM or patch must be refreshed. Files are not pruned when an upstream patch link disappears.

Unsafe paths, symlinks, and local copy errors stop immediately. Patch HTTP or network failures are collected while later downloads continue. The complete report is still written, every failed URL is diagnosed, and the command exits non-zero if any patch failed. A rerun retains successful files and only attempts missing filenames. See the patch candidate mirror guide for the complete behavior.

Scope

This project packages and operates upstream RAChecker. It does not modify or reimplement RAChecker, guarantee upstream ROM hashing or achievement behavior, commit personal ROMs or host paths, or publish container images. The optional mismatch utility reads operator-selected local filenames only when explicitly invoked; repository verification uses synthetic data and never accesses a real ROM library.

License

This repository's original deployment assets and utilities are available under the MIT License. RAChecker, RAHasher, and other fetched third-party components remain covered by their respective upstream licenses; this license does not claim or relicense those projects.

About

Docker and Docker Compose packaging for RAChecker with Linux RAHasher support and ROM mismatch reporting

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages