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

> Learn how Apollo PS4 implements multi-user support. Discover how user_id and account_id are configured to manage save data, trophies, and online storage for each PS4 profile.

- Repository: [Damián Parrino/apollo-ps4](https://github.com/bucanero/apollo-ps4)
- Tags: deep-dive
- Published: 2026-02-26

---

**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`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/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:

```c
// 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`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c):

```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`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c), both identifiers are written to the user's save data partition:

```c
// 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`](https://github.com/bucanero/apollo-ps4/blob/main/main.c), the `update_hdd_path()` and `update_trophy_path()` functions format paths using `apollo_config.user_id`:

```c
// 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:

```c
// 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`](https://github.com/bucanero/apollo-ps4/blob/main/source/sqlite_db.c), the `appdb_rebuild()` function creates per-user table names:

```c
// 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`](https://github.com/bucanero/apollo-ps4/blob/main/source/sfo.c) injects both identifiers into the `param.sfo` file:

```c
// 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`](https://github.com/bucanero/apollo-ps4/blob/main/source/menu_about.c) displays both identifiers:

```c
// 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`](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h) | Defines `app_config_t` structure containing `user_id` and `account_id` |
| [`main.c`](https://github.com/bucanero/apollo-ps4/blob/main/main.c) | Global `apollo_config` instance, `initPad()`, path-building helpers |
| [`source/settings.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/settings.c) | Load/save configuration, `sceUserServiceGetNpAccountId` resolution |
| [`source/sfo.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/sfo.c) | `sfo_patch_user_id()` and `sfo_patch_account()` functions |
| [`source/sqlite_db.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/sqlite_db.c) | Per-user database handling in `appdb_rebuild()` |
| [`source/exec_cmd.c`](https://github.com/bucanero/apollo-ps4/blob/main/source/exec_cmd.c) | Remote FTP/HTTP command construction using `account_id` |
| [`source/menu_about.c`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/include/settings.h) and instantiated in [`main.c`](https://github.com/bucanero/apollo-ps4/blob/main/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`](https://github.com/bucanero/apollo-ps4/blob/main/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.