> ## Documentation Index
> Fetch the complete documentation index at: https://runelite.zip/llms.txt
> Use this file to discover all available pages before exploring further.

# Profiles

> Encrypted account profiles, auto-login, saved proxies, and preferred worlds.

Profiles is a Packet Utils tab in the shared n3 sidebar. It stores account credentials in an encrypted profile store, applies them to the login screen, and manages per-profile proxy and world preferences.

## What you can do from the tab

* Add, edit, delete, and unlock profiles
* Trigger a login through the Jagex OAuth flow in your system browser
* Bulk import profiles by pasting typed rows
* Add, edit, delete, and test saved SOCKS5 proxies
* Set a preferred world or world type per profile

The tab also embeds a filtered RuneLite configuration panel that renders **Auto Login** and the secret **Unlock Password** field from the `n3profiles` group.

## Architecture

| Component                   | Role                                                                            |
| --------------------------- | ------------------------------------------------------------------------------- |
| `N3ProfilesPanel`           | Login-screen profile list, unlock, bulk import, proxy management, OAuth trigger |
| `N3ProfilesConfig`          | Config group `n3profiles` (`autoLog`, `unlockPassword`, `profilesData`, `salt`) |
| `ProfileStore`              | Shared encrypted store other plugins can inject and read                        |
| `StoredProfile`             | One typed credential line plus proxy and world preferences                      |
| `ProfileStorageCodec`       | AES-GCM v3 encoding, v2 decoding, legacy migration                              |
| `ProxyStore` / `SavedProxy` | Separately salted saved-proxy library unlocked by the same password             |
| `ProfileLoginService`       | Applies a profile to the client                                                 |
| `JagexLoginAdapter`         | Revision-sensitive reflection hooks for the Jagex login screen                  |
| `jagexauth/`                | Browser-guarded OAuth flow with a local callback receiver                       |

## Credential storage

`ProfileStore` derives an AES key from your unlock password with PBKDF2WithHmacSHA256 (100,000 iterations, 128-bit keys) and encrypts the profile blob with AES-GCM under the `n3profiles` group (`profilesData` plus `salt`). Its only dependency is the RuneLite `ConfigManager`, so other plugins can inject their own instance and read the same store without a shared singleton.

Profile lines are typed:

```text theme={null}
LEGACY:label:login:password:pin:proxyLabel:preferredWorld:worldType
JAGEX:displayName:characterId:sessionId:userHash:proxyLabel:preferredWorld:worldType
```

Untyped legacy lines (`label:login:password[:pin]`) decode as `LEGACY` and re-encode on first load.

### Store API

* `load(char[] password)` decrypts, migrates, and parses profiles. Throws on a wrong password.
* `save(List<StoredProfile>, char[] password)` re-encrypts and persists.
* `loadWithConfiguredPassword()` decrypts with the configured unlock password and returns an empty list on any failure instead of throwing.
* `hasConfiguredPassword()` reports whether auto-unlock is configured.

The plugin never persists, logs, serializes, or screenshots the supplied password.

## Bulk import

Unlock the Profiles tab and select **Bulk Import** to paste multiline input. The importer accepts only typed rows:

```text theme={null}
LEGACY:label:login:password[:pin][:proxyLabel][:preferredWorld][:worldType]
JAGEX:label:characterId:sessionId[:userHash][:proxyLabel][:preferredWorld][:worldType]
```

Rules the importer enforces:

* Blank lines are ignored.
* Legacy rows require a login and password. The PIN must be blank or exactly four digits.
* Jagex rows require a character ID and session ID. The user hash is optional.
* Preferred worlds must be non-negative integers.
* World types must be `MEMBERS`, `F2P`, `PVP`, or blank.

Malformed rows and duplicate labels are skipped and reported in the import summary.

## Saved proxies

Saved proxies live in the `n3proxies` config group with their own random salt but share the active profiles unlock password. The unlocked tab can add, edit, delete, and test uniquely labelled SOCKS5 proxies. A test performs a real SOCKS5 negotiation, optional username/password authentication, and a tunneled connection request on a background executor.

Deleting a proxy also clears profile references to its label.

## Login behavior

`ProfileLoginService` applies two profile kinds:

* `applyLegacyProfile(username, password, autoLogin)` sets the client username and password fields and dispatches Enter keys when `autoLogin` is on. A client already on the legacy login form is left as-is.
* `applyJagexProfile(sessionId, characterId, displayName)` applies a Jagex session through the reflection adapter, selects login index 10, and attempts the shared welcome-screen continue helper. It throws `ReflectiveOperationException` when the hooks are unavailable.
* `resetToLegacyLogin()` reverts a Jagex login screen to the legacy form. This is the only path that performs the Jagex-to-legacy transition, so a client already in legacy mode is never reset.
* `isJagexLoginAvailable()` probes adapter support.

The adapter validates cached field shapes against the injected class loader before use and rolls back every reflected field if a transition fails. Missing, ambiguous, or unsafe login-index hooks make the adapter unsupported.

Applying a profile that names a saved proxy immediately replaces global proxy routing. A preferred world is selected on the login screen before credentials are submitted. With no explicit world, a non-empty world-type preference selects the first matching world-list entry. An explicit world that conflicts with the configured type is rejected.

<Tip>
  Set the **Unlock Password** field if you want profiles available without manual unlock, for example for auto-login. Leaving it blank requires unlocking the tab by hand each session.
</Tip>
