Pocket Harbor is a controller-first game library manager for Linux handhelds. The portable core is designed for multiple operating-system targets; the current release target is DarkOS ARM64, the only environment tested end to end so far. It searches supported stores, installs games into the target's ROM directory, and manages one-card and two-card libraries.
The device release is self-contained. Python, uv, a torrent client, and a package manager are not required on the handheld.
Contributing explains development setup, project rules, and required checks.
- Search by a case-insensitive title prefix, or submit an empty search to list a catalogue.
- Search one platform or all platforms supported by the selected store.
- Choose Vimm or Minerva Archive once and change the preferred store later in Settings.
- Cache complete store catalogues by store and platform for fast offline empty and prefix searches.
- Download to the correct target ROM folder on SD1 or SD2.
- Queue up to three background downloads, with persistent pause, resume, retry, and cancel controls.
- Show game compatibility information before downloading when a reliable catalogue match exists.
- Cache compatibility metadata locally and refresh it only when requested from Settings.
- Install BIOS files bundled with a game first, then check required firmware across both SD cards.
- Offer required missing BIOS through RetroBIOS, plus a manual BIOS search from the main menu.
- Scan, update, and delete installed games, including grouped
.cueand.m3ufiles. - Resume interrupted HTTP and Minerva downloads automatically after reopening the tool.
- Refresh the EmulationStation game list once when the TUI closes after a library change.
- Update the application from Settings, preserving preferences and rolling back if the new version fails its first launch.
- Use Minerva torrents through the built-in Python BitTorrent client; no external downloader is needed.
- Download
pocket-harbor-<version>-darkos-arm64.zipfrom the latest release. - Extract the ZIP on your computer.
- Copy everything inside its
toolsdirectory into the ROM card'stoolsdirectory. - Put the card back into the handheld.
- Open the downloader from Options → Tools in EmulationStation.
The card must contain:
tools/
├── Pocket Harbor.sh
└── pocket-harbor/
├── pocket-harbor
├── ca-certificates.crt
└── _internal/
Important
Versions before 2.0 used different internal package and storage paths. They cannot update to this
release in place. Remove the earlier installation, copy the complete new tools tree, and
configure the application again. Releases starting with 2.0 preserve
tools/pocket-harbor/.downloads during normal updates.
| Handheld control | Action |
|---|---|
| D-pad or stick up/down | Move through menus; hold to scroll |
| D-pad or stick left | Back |
| D-pad or stick right | Select |
| A | Select |
| B or Select | Go back one screen |
| L1 / R1 | Previous / next page |
| X | Submit the on-screen keyboard, including an empty search |
| Y | Delete the last character |
| Start | Select in menus; ignored by the on-screen keyboard |
On a computer, use the arrow keys, Enter, Escape, Page Up, Page Down, Backspace, and normal typing.
The on-screen keyboard uses a fixed 12-column grid with equally sized character keys. Use its
aA, #+=, and ÁÉ keys to switch case or enter punctuation, symbols, and accented characters.
On first launch, choose the default store. Then:
- Select Search the library.
- Choose a platform or All platforms.
- Enter the beginning of a title. Matching is case-insensitive.
- Press X. An empty value lists the selected catalogue.
- Review the game details and compatibility information.
- Select Download and choose a memory card when both SD1 and SD2 are available.
Downloads run in the background and are completed in a staging directory before being moved. Open Downloads from the main menu to monitor progress or pause, resume, retry, and cancel jobs. Unfinished jobs resume after Pocket Harbor is reopened, while completed entries disappear after the TUI exits. Each queued job keeps its original store even if the preferred store changes. Existing ROMs are not silently overwritten. If a store responds with HTTP 429, the job waits and retries automatically with increasing randomized delays instead of failing or blocking another store.
After a game is installed, the downloader processes any firmware supplied in its explicit bios/
directory before checking for anything else. It then compares the game's required firmware with
the BIOS directories on both SD cards. A valid copy on either card counts as installed, so it does
not ask to download a duplicate.
If required firmware is still missing or has the wrong checksum, the TUI explains what was found and lets you either download the verified file from RetroBIOS or keep the game without it. Optional firmware is not prompted automatically. Known ROM-local exceptions are installed in their required locations.
Use Search and download BIOS on the main menu to browse all supported firmware, including
optional files, and install one manually. The RetroBIOS catalogue is downloaded on the first BIOS
operation and cached under .downloads/retrobios. It is not refreshed automatically; use
Settings → Update RetroBIOS catalogue when you want the latest upstream metadata.
The game compatibility catalogue follows the same policy: it is fetched on first use, stored locally, and refreshed only through Settings → Update compatibility catalogue. Settings marks RetroBIOS and compatibility metadata stale after the configured catalogue lifetime, which defaults to seven days. Their saved copies remain usable until you choose to update them.
- Vimm provides direct downloads and platform catalogues.
- Minerva Archive uses its RetroAchievements collection. The application downloads only the selected file's verified torrent pieces. Availability depends on public trackers and seeders, and the public IP address of the handheld is visible to the swarm while downloading.
Each store's complete game catalogue is saved as structured JSON under
.downloads/game-catalogues/<store>/<platform>.json. A fresh cache is reused for empty and
case-insensitive prefix searches without contacting the store. After the configured lifetime, the
next search refreshes it automatically; if the store is unavailable, the last valid catalogue is
used. Use Settings → Refresh store game catalogue to replace a selected store/platform cache
immediately.
If Minerva reorders a torrent, the downloader finds a unique filename automatically. When a game was renamed or is ambiguous, the TUI shows the closest torrent files with their position, path, size, and title similarity so you can explicitly choose one or safely cancel.
The preferred store applies to searches, direct downloads, and installed-game updates. Change it through Settings → Change download store.
Open Manage installed games, choose the memory card and platform, then select a game.
- Update from remote searches the preferred store using the installed title. The old files stay in place until the queued background replacement finishes and is installed successfully.
- Delete from device removes the selected game after confirmation. Files referenced by
.cueand.m3uplaylists are treated as one game group.
After an install, update, or deletion, continue using the application normally. EmulationStation is refreshed once after you confirm Exit; a device reboot is not normally required.
Open Settings → Check for application update. If a newer release is available, confirm the download and wait for the TUI to return to EmulationStation. Reopen the tool to verify the new version.
Preferences and cached data are preserved. The previous application remains available until the updated TUI exits successfully for the first time. If that launch fails, the launcher restores the previous version automatically.
Local use requires uv and a terminal with curses support. WSL2 is recommended on Windows.
uv sync --frozen
uv run phUse the offline demonstration when you do not want to contact a real store or modify a ROM library:
uv run python -m scripts.run_local_demoThe demo creates temporary SD1 and SD2 directories under .local-test and serves small fake game
files from localhost.
Useful automation commands:
uv run ph search GBA "advance"
uv run ph search GBA
uv run ph --store minerva search ALL "mario"
uv run ph download --platform GBA --roms-directory .local-test/sd1 DETAIL_URLThe live RetroBIOS integration test downloads current metadata and one small firmware file from the real upstream service:
uv run pytest -m live tests/e2e/test_live_retrobios.py -vRun it only where outbound GitHub access is available. The ordinary test suite remains offline.
| Variable | Purpose | Default |
|---|---|---|
PH_VIMM_BASE_URL |
Vimm service root | https://vimm.net |
PH_MINERVA_BASE_URL |
Minerva browse service root | https://minerva-archive.org |
PH_MINERVA_TORRENT_BASE_URL |
Minerva torrent metadata root | Minerva CDN |
PH_STORES |
Comma-separated enabled stores | vimm,minerva |
PH_DOWNLOAD_DIR |
Temporary downloads and preferences | Current directory; private application directory on device |
PH_ROMS_DIR |
One explicit ROM root | Auto-detect |
PH_ROMS_DIRS |
Multiple ROM roots separated by the OS path separator | Auto-detect /roms2, then /roms |
PH_TIMEOUT |
Network timeout in seconds | 30 |
PH_LOG_FILE |
Diagnostic log destination | Set by the device launcher |
PH_LOG_LEVEL |
DEBUG, INFO, WARNING, or ERROR |
INFO |
PH_TARGET_OS |
Linux integration profile | darkos |
Only darkos is currently registered. An unknown value fails at startup rather than silently using
paths or update packages from the wrong operating system. See Linux target support.
Minerva-specific transfer limits can be edited from Settings → Minerva BitTorrent settings when Minerva is selected.
The interface language, default ROM card, per-store console-folder mappings, shared BIOS folder, network timeout, catalogue cache lifetime, application log level, and application file logging can also be changed from Settings. Folder mappings use selectors populated from known folders and folders discovered on the active cards. The global concurrent-download limit accepts 1-8, defaults to 3, and applies after restarting Pocket Harbor. English is the default; German, Spanish, French, Italian, and Portuguese are included. Boolean settings use a True/False picker and numeric settings use a number-only keyboard. The default catalogue lifetime is seven days. HTTP 429 retry settings expose the initial delay, maximum delay, and jitter; the maximum delay defaults to 3,600 seconds (one hour).
On the handheld, structured application logs and launcher diagnostics are stored in:
tools/pocket-harbor/pocket-harbor.log
The log rotates automatically so repeated use does not grow one file indefinitely. Use
Settings → Application log level to choose DEBUG, INFO, WARNING, or ERROR, and
Settings → Write logs to file to enable or disable application records. Environment variables
provide the initial local defaults; saved TUI preferences take precedence. After logging is enabled,
the confirmation screen shows the exact active path. If the launcher path is not writable, Pocket
Harbor falls back to tools/pocket-harbor/.downloads/pocket-harbor.log and records the reason there.
Common problems:
- The tool immediately returns to EmulationStation: copy the complete release again and inspect the diagnostic log.
- No ROM partition is found: verify that
/romsor/roms2is mounted, or setPH_ROMS_DIRfor local use. - Minerva reports no peers: public seeders may be offline, or the network may block tracker UDP or peer TCP traffic. Try again later or switch to Vimm.
- The terminal is too small locally: resize it to at least 40 columns by 15 rows.
- Windows reports that curses is unavailable: run the application under WSL2.
Only download content you have the legal right to use.