Apollo PS4 Multi-User Support Implementation: Configuring user_id and account_id

Apollo PS4 implements multi-user support by storing the active user's user_id and account_id in a global app_config_t structure, using these identifiers to isolate save data, trophy databases, and online storage paths per PS4 user profile.

The multi-user support implementation in Apollo PS4 ensures that each PlayStation 4 user profile maintains isolated access to saves, trophies, and configuration data. This open-source tool by bucanero/apollo-ps4 retrieves the active user's identifiers directly from the PS4 OS and propagates them throughout the codebase to construct user-specific file paths and database connections.

Where the Identifiers Live

The user_id and account_id are defined in include/settings.h as members of the app_config_t structure:

  • user_id (uint32_t): The PSN user ID used for local sandboxing (declared at lines 40-42)
  • account_id (uint64_t): The NP (Network Platform) account ID for online services (declared at lines 42-44)

Both fields are part of the global apollo_config instance declared in main.c, making the identifiers accessible throughout the application.

How the Identifiers Are Obtained

Retrieving user_id

At startup, Apollo reads the active user ID from the pad controller configuration:

// main.c – after SDL & pad init (line 246)
apollo_config.user_id = orbisPadGetConf()->userId;

The orbisPadGetConf() function returns a structure containing the OS-assigned user ID for the currently active user profile.

Resolving account_id

The account_id is resolved from the user_id during configuration loading in source/settings.c:

// settings.c – load_app_settings() (lines 73-76)
sceUserServiceGetNpAccountId(config->user_id, &config->account_id);

The sceUserServiceGetNpAccountId system call maps the local 32-bit user ID to the globally unique 64-bit NP account ID. This value persists to the settings file whenever the configuration is saved.

Persistence of the IDs

When settings are saved via save_app_settings() in source/settings.c, both identifiers are written to the user's save data partition:

// settings.c – inside save_app_settings() (line 404)
file_data->user_id    = config->user_id;
file_data->account_id = config->account_id;

Each PS4 user maintains a separate settings.bin file, ensuring complete isolation of configuration data between profiles.

How the IDs Are Used Throughout the Codebase

File-System Paths

The user_id is embedded in paths for save data and trophy databases. In main.c, the update_hdd_path() and update_trophy_path() functions format paths using apollo_config.user_id:

// main.c – update_trophy_path() (lines 44-51)
snprintf(path, sizeof(path), TROPHY_DB_PATH, apollo_config.user_id);

This generates user-specific paths like /system_data/savedata/%08x/db/user/savedata.db.

Online Storage Paths

When online mode is enabled, the account_id is appended to FTP URLs to create isolated cloud folders:

// main.c – update_db_path() (lines 55-61)
sprintf(path, "%s%016lX/", apollo_config.ftp_url, apollo_config.account_id);

SQLite Database Operations

In source/sqlite_db.c, the appdb_rebuild() function creates per-user table names:

// sqlite_db.c – appdb_rebuild() (lines 207-236)
snprintf(path, sizeof(path),
         "SELECT 1 FROM tbl_appbrowse_%010d WHERE titleId='%s'",
         userid, dirp->d_name);

SFO File Patching

When patching save files, source/sfo.c injects both identifiers into the param.sfo file:

// sfo.c – patch_sfo() calls these:
sfo_patch_user_id(sfo, patches->user_id);     // writes USER_ID into PARAMS
sfo_patch_account(sfo, patches->account_id);  // writes ACCOUNT_ID field

This ensures the PS4 OS recognizes the save as belonging to the active user profile.

UI Display

The About screen in source/menu_about.c displays both identifiers:

// menu_about.c (lines 71-72)
snprintf(user_id_str, sizeof(user_id_str), "%08x", apollo_config.user_id);
snprintf(account_id_str, sizeof(account_id_str), "%016lx", apollo_config.account_id);

Key Files

File Purpose
include/settings.h Defines app_config_t structure containing user_id and account_id
main.c Global apollo_config instance, initPad(), path-building helpers
source/settings.c Load/save configuration, sceUserServiceGetNpAccountId resolution
source/sfo.c sfo_patch_user_id() and sfo_patch_account() functions
source/sqlite_db.c Per-user database handling in appdb_rebuild()
source/exec_cmd.c Remote FTP/HTTP command construction using account_id
source/menu_about.c UI display of both identifiers

Summary

  • Apollo PS4 stores the active user's user_id (32-bit) and account_id (64-bit) in the global app_config_t structure named apollo_config.
  • The user_id is obtained from orbisPadGetConf()->userId at startup, while account_id is resolved via sceUserServiceGetNpAccountId() during settings loading.
  • Both identifiers persist to a per-user settings.bin file, ensuring isolation between PS4 user profiles.
  • Throughout the codebase, these IDs construct file-system paths, SQLite table names, FTP URLs, and SFO file patches, enabling complete multi-user support for saves, trophies, and online storage.

Frequently Asked Questions

How does Apollo determine which PS4 user is currently active?

Apollo reads the active user ID directly from the PS4 OS pad configuration. During initialization in main.c, the code assigns apollo_config.user_id = orbisPadGetConf()->userId, which returns the 32-bit user identifier assigned by the system to the currently logged-in profile.

What is the difference between user_id and account_id in Apollo?

The user_id is a 32-bit local identifier used by the PS4 OS for sandboxing and local user management, while the account_id is a 64-bit NP (Network Platform) identifier used for online services and global account recognition. Apollo uses user_id for local file paths and database names, and account_id for remote FTP/HTTP storage paths.

Where are the user identifiers stored in Apollo's configuration?

Both identifiers are stored in the global apollo_config instance of type app_config_t, defined in include/settings.h and instantiated in main.c. They persist to a binary settings file (settings.bin) located in the user's save data partition, with each PS4 user maintaining a separate copy of this file.

How does Apollo prevent save data from leaking between different user profiles?

Apollo enforces isolation by embedding the user_id into every file-system path, database query, and SFO patch operation. Functions like update_hdd_path() and update_trophy_path() in main.c format paths using the current user's ID, while sfo_patch_user_id() ensures that modified save files are stamped with the correct user identifier, preventing the PS4 OS from cross-associating saves between profiles.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →