# PlatformAdminBootstrapService

**Kind:** Service

**Source:** [`atloria-monorepo/apps/api/src/platform/platform-admin-bootstrap.service.ts`](https://github.com/sherkety/atloria/blob/main/atloria-monorepo/apps/api/src/platform/platform-admin-bootstrap.service.ts#L21)

Declarative bootstrap of platform admins from `PLATFORM_ADMIN_EMAILS` (a
comma-separated k8s-secret value). Runs once on boot and treats the env list
as the source of truth:
  - promotes listed users that exist,
  - demotes any current platform admin NOT on the list (so revocation is just
    an edit to the secret + redeploy).

SAFETY: when the list is empty/unset this is a NO-OP — it never mass-demotes.
That protects against a misconfigured/blank secret wiping out access; recovery
is the one-off `scripts/grant-platform-admin.ts` or restoring the secret.

Emails not matching any user are logged, not created — a platform admin must
already be a real member of some org.

`PlatformAdminBootstrapService` reconciles platform administrator access from the `PLATFORM_ADMIN_EMAILS` environment variable during application startup. It promotes existing users listed in the comma-separated secret and demotes current platform admins omitted from it, making the secret the authoritative access list. An empty or unset value is intentionally a no-op to prevent accidental mass revocation.

## Methods

| Method | Signature | Returns | Description |
|---|---|---|---|
| `onModuleInit` | `onModuleInit()` | `Promise<void>` |  |
| `reconcile` | `reconcile()` | `Promise<{ promoted: number; demoted: number; unmatched: string[] }>` | Exposed for tests; called on boot. |

## Dependencies

- `PrismaService`
- `ConfigService`

## When something fails

- `PlatformAdminBootstrapService` handles failure in 1 place: it logs it and continues in all 1.

## Diagram

```mermaid
sequenceDiagram
    participant App as NestJS Application
    participant Service as PlatformAdminBootstrapService
    participant Env as PLATFORM_ADMIN_EMAILS
    participant Users as User Repository

    App->>Service: onModuleInit()
    Service->>Env: Read and parse email list

    alt Email list is empty or unset
        Service-->>App: No-op (preserve existing admins)
    else Email list contains emails
        Service->>Users: Find users matching configured emails
        Users-->>Service: Existing users and unmatched emails

        Service->>Users: Promote matched users to platform admin
        Service->>Users: Find current platform admins
        Service->>Users: Demote admins not in configured list

        Service-->>App: Reconciliation result
    end
```

## Usage

```ts
import { PlatformAdminBootstrapService } from './platform-admin-bootstrap.service';

// Normally invoked automatically by NestJS through onModuleInit().
@Injectable()
export class PlatformMaintenanceService {
  constructor(
    private readonly platformAdminBootstrap: PlatformAdminBootstrapService,
  ) {}

  async reconcilePlatformAdmins() {
    const result = await this.platformAdminBootstrap.reconcile();

    console.log(
      `Promoted: ${result.promoted}, demoted: ${result.demoted}`,
    );

    if (result.unmatched.length > 0) {
      console.warn(
        'Configured platform admin emails do not match existing users:',
        result.unmatched,
      );
    }

    return result;
  }
}
```

## AI Coding Instructions

- Treat `PLATFORM_ADMIN_EMAILS` as the source of truth only when it contains at least one valid configured value; never change the empty/unset-list no-op safety behavior.
- Do not create users for unmatched emails. Platform administrators must already exist as members of an organization.
- Preserve the startup integration through `onModuleInit()` so reconciliation runs automatically after the NestJS module initializes.
- When changing reconciliation logic, ensure both promotion and revocation paths remain idempotent and return accurate `promoted`, `demoted`, and `unmatched` counts.
- Use `scripts/grant-platform-admin.ts` or restore the configured secret for recovery scenarios rather than weakening bootstrap safety checks.

## Relationships

- DEPENDS_ON → `PrismaService`
- DEPENDS_ON → `configservice`

## Referenced By

- `PlatformModule` (MODULE_PROVIDES)
- `PlatformModule` (MODULE_EXPORTS)
