# Multi-Server License Authority Setup

## Nodes

Preferred primary:

`https://parallaxware.pk/coreapp`

Backup / secondary:

`https://ipos.com.pk/capp`

Both servers run the same codebase. The difference is the `authority` section in `config.php`.

## Server 1 Config

Use this on `parallaxware.pk/coreapp`:

```php
'authority' => [
    'server_id' => 'P1',
    'server_name' => 'Parallaxware Primary Authority',
    'server_url' => 'https://parallaxware.pk/coreapp',
    'preferred_primary_url' => 'https://parallaxware.pk/coreapp',
    'bootstrap_servers' => [
        'https://parallaxware.pk/coreapp',
        'https://ipos.com.pk/capp',
    ],
    'sync_secret' => 'USE_THE_SAME_LONG_RANDOM_SECRET_ON_BOTH_SERVERS',
    'offline_grace_days' => 21,
    'server_list_valid_days' => 31,
],
```

## Server 2 Config

Use this on `ipos.com.pk/capp`:

```php
'authority' => [
    'server_id' => 'S1',
    'server_name' => 'IPOS Backup Authority',
    'server_url' => 'https://ipos.com.pk/capp',
    'preferred_primary_url' => 'https://parallaxware.pk/coreapp',
    'bootstrap_servers' => [
        'https://parallaxware.pk/coreapp',
        'https://ipos.com.pk/capp',
    ],
    'sync_secret' => 'USE_THE_SAME_LONG_RANDOM_SECRET_ON_BOTH_SERVERS',
    'offline_grace_days' => 21,
    'server_list_valid_days' => 31,
],
```

## Database

For an existing database, import:

`database/authority_network_migration.sql`

For a fresh deployment, import the latest no-install SQL first, then import the migration if it is not already included.

## Local XAMPP Setup

For local development, use:

`config.local.example.php`

Copy it as `config.php` if needed. It uses:

- `base_url`: `auto`
- `authority.server_id`: `LOCAL`
- `authority.server_url`: `auto`
- `authority.preferred_primary_url`: `auto`
- database: `core_license_system`
- MySQL host: `127.0.0.1`
- MySQL port: `3307`
- MySQL user: `root`

The app will detect URLs such as:

- `http://localhost/core_license_system`
- `http://localhost/core_license_system/public`
- `http://127.0.0.1/core_license_system`

For a fresh local database, import the generated local authority SQL file:

`database/core_license_system_LOCAL_AUTHORITY_NO_INSTALL_*.sql`

## Admin Flow

Open `Server Control`.

Use it to:

- View current server, preferred primary, active primary, sync version, and server-list version.
- Generate a signed server list.
- Promote the current server to active primary during disaster recovery.
- Disable or re-enable a failed server.
- Add a third server.
- View pending sync changes and sync logs.

## Client Flow

Client apps should bootstrap with:

```php
$client = new CoreLicenseClient([
    'https://parallaxware.pk/coreapp',
    'https://ipos.com.pk/capp',
], 'APP_CODE', __DIR__.'/license-storage', [
    // Recommended: seed trusted server-list key IDs here after first controlled setup.
    // 'core-key-YYYYMMDD-HHMMSS' => base64_decode('PUBLIC_KEY_BASE64'),
]);
```

The client:

- Loads saved signed server list.
- Verifies its signature.
- Falls back to hardcoded bootstrap URLs.
- Fetches `/api/server-list` from any healthy server.
- Verifies the server-list signature.
- Tries active servers for license verification.
- Accepts only signed license tokens.
- Verifies token signature, app code, install ID, domain, base path, fingerprint, expiry, and local grace.

## Disaster Recovery Test Steps

1. Normal primary verification: keep both servers enabled, verify from `parallaxware.pk/coreapp`.
2. Backup verification: block primary network access from a test client and verify through `ipos.com.pk/capp`.
3. Primary down scenario: make primary unreachable and confirm client uses the saved or bootstrap backup URL.
4. Promote backup: login to `ipos.com.pk/capp`, open Server Control, click Promote This Server to Active Primary.
5. Old primary recovery: when `parallaxware.pk/coreapp` returns, keep it in recovering/disabled state until it pulls newer change-log versions from `ipos.com.pk/capp`.
6. Add third server: install same code, add it in Server Control, regenerate server list.
7. Signed token verification: tamper with a token byte and confirm client rejects it.
8. Signed server-list verification: tamper with `server_list.json` and confirm client rejects it.
9. 21-day offline grace: disconnect all servers and confirm only a valid cached signed token within `grace_until` can be used.

## Security Notes

- Private keys stay only on the server. In production, set `security.signing_key_path` to a folder outside `public_html`, such as `/home/parallax/core_license_private/storage/keys` or `/home4/iposcomp/core_license_private/storage/keys`.
- Client apps must never include private keys.
- Server-to-server sync endpoints require HMAC headers with nonce and timestamp.
- Old primary must not overwrite backup changes after disaster recovery. It should pull changes with the highest sync version before becoming healthy again.
