# Multi-System Support The pttclient can connect to multiple independent operator Systems at runtime without rebuilding the APK. This document explains how the system list is managed, how trust is established for imported systems, and how switching works. --- ## System List Architecture The client maintains a list of **Systems**, each containing one or more **Servers**. This is managed by `ServerListManager`. ``` System List ├── Built-in System (from APK build) │ ├── Default Server (from R.string.ptt_server) │ └── Additional Servers (user-added, persisted in SharedPreferences) │ └── Imported Systems (added via QR scan) ├── Server A └── Server B ``` A **System** is identified by its `SystemID = SHA-256(System CA public key DER)[0:32]`. A **Server** is identified by its base URL (e.g., `https://ptt.example.com:31000`). --- ## Built-in System The built-in system is derived from the System CA certificate bundled in `assets/certs/` at build time. `ServerListManager.getBuiltInSystem()` reads the first `.crt` or `.pem` file (that is not the Platform Issuing CA) from `assets/certs/`, passes it to `SystemCaImporter.verifyAndParseCaCert()`, and constructs a `SystemEntry` with: - `systemId`: derived from the cert's public key - `label`: the CN from the cert's subject - `caCertPem`: the full PEM string - `builtIn: true` - Initial server from `R.string.ptt_server` --- ## Importing a New System via QR Code A ptt-server exposes a QR code at `GET /qr` containing: ```json { "v": 1, "url": "https://ptt.example.com:31000", "label": "Example System" } ``` The import flow in `ServerManagementActivity`: 1. User scans QR code with the built-in scanner. 2. App calls `SystemCaImporter.fetchVerifyAndParse(context, okHttpClient, baseUrl + "/system-root-ca.crt")`. 3. The importer fetches the System CA PEM from the server. 4. **Verification**: the imported System CA must be signed by the **System CA Signing CA** (the cert in `assets/certs/` whose filename contains `system_ca_signing`). This prevents rogue CAs from being imported. 5. If verification passes, a new `SystemEntry` is created with the imported CA PEM and the server URL. 6. The system is persisted to `SharedPreferences` (key: `ptt_server_list_v2`). **Maximum**: 15 imported systems (`ServerListManager.MAX_IMPORTED_SYSTEMS = 15`). --- ## Switching Active System/Server When the user selects a different server in `ServerManagementActivity`: ```java // 1. Update PlatformTrustManager with the new system's CA PEM PlatformTrustManager.setActiveSystemCaPem(picked.system.getCaCertPem()); // 2. Switch active selection (persists in SharedPreferences) app.switchActiveSelection(picked.system.getSystemId(), picked.server.getBaseUrl(), systemChanged); // 3. Restart SocketService stopService(stopIntent); startForegroundService(startIntent); ``` `switchActiveSelection()` calls `resetNetworkClients(resetTrustManager)`, which: - Disconnects and nulls the active Socket.IO socket. - Nulls the OkHttpClient. - If `resetTrustManager=true`, calls `PlatformTrustManager.reset()` (clears cached SSLContext so it rebuilds with the new system CA on next use). - Clears all stale server-derived UI state (system name, TG assignments, etc.). --- ## Trust for Multiple Systems `PlatformTrustManager` builds an SSLContext trusting: 1. All `.pem`/`.crt` files in `assets/certs/` (bundled System CAs + Platform Issuing CA). 2. The `activeSystemCaPem` set by `setActiveSystemCaPem()` at runtime (the imported system's CA). 3. The Android system certificate store (for public CAs used by reverse proxies). `ServerTrustManager` (Layer 2) loads **all** `.pem` and `.crt` files from `assets/certs/` as trusted CAs, including the System CA Signing CA and any System CA bundled at build time. This means Layer 2 validation is anchored to the bundled CA certificates. For imported systems, Layer 2 validation succeeds if the server's identity certificate chains to a CA present in the bundled `assets/certs/` — which will be true if the imported System CA was co-signed by the same System CA Signing CA bundled in the APK. `ServerTrustManager` does not dynamically pick up runtime-imported System CAs (unlike `PlatformTrustManager` which accepts the `activeSystemCaPem` for Layer 1 TLS). --- ## Persistence | Data | Storage | |---|---| | System list (imported systems) | `SharedPreferences` key `ptt_server_list_v2` (JSON array) | | Active system ID | `SharedPreferences` key `active_system_id` | | Active server URL | `SharedPreferences` key `active_server_url` | | Extra servers for built-in system | `SharedPreferences` key `ptt_server_list_builtin_` | | Limits | `MAX_IMPORTED_SYSTEMS = 15`; `MAX_SERVERS_PER_SYSTEM = 10` |