import type { EkklesiaUserId } from "@ekklesia/contracts"; import { eq, sql } from "drizzle-orm"; import { ekklesiaCoreUser } from "../identity/schema.js"; import type { AuthorizationDrizzleDatabase } from "./database.js"; import { ekklesiaCoreAuthorizationAudit, ekklesiaCorePlatformOwner, ekklesiaCorePlatformOwnerBootstrap, PLATFORM_OWNER_BOOTSTRAP_SINGLETON_ID, } from "./schema.js"; export type PlatformOwnerBootstrapErrorCode = | "PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED" | "PLATFORM_OWNER_BOOTSTRAP_FAILED" | "PLATFORM_OWNER_BOOTSTRAP_USER_NOT_FOUND"; const bootstrapErrorMessages: Readonly> = Object.freeze({ PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED: "Platform-owner bootstrap has already been used and cannot be used again.", PLATFORM_OWNER_BOOTSTRAP_FAILED: "Platform-owner bootstrap could not be completed.", PLATFORM_OWNER_BOOTSTRAP_USER_NOT_FOUND: "The target user does not exist.", }); /** Never carries the underlying database error, SQL text, host, port, path, or stack. */ export class PlatformOwnerBootstrapError extends Error { readonly code: PlatformOwnerBootstrapErrorCode; constructor(code: PlatformOwnerBootstrapErrorCode) { super(bootstrapErrorMessages[code]); this.name = "PlatformOwnerBootstrapError"; this.code = code; this.stack = `${this.name}: ${this.message}`; Object.freeze(this); } } function isPlatformOwnerBootstrapError(error: unknown): error is PlatformOwnerBootstrapError { return error instanceof PlatformOwnerBootstrapError; } /** A fixed, sanitized, non-user actor identity for the audit trail — distinguishable from both an * ordinary "system" action with no specific actor and from any real `EkklesiaUserId`, so an * auditor can tell this owner grant came specifically from the bootstrap command. Never a real * user id and never derived from caller input. */ const BOOTSTRAP_ACTOR_TYPE = "system"; const BOOTSTRAP_ACTOR_ID = "authorization-bootstrap-cli"; // A dedicated advisory-lock key pair, distinct from `core/src/migrations/postgres-database.ts`'s // migration lock, so bootstrap serialization can never collide with or be blocked by unrelated // migration locking. `pg_advisory_xact_lock` is transaction-scoped: it is released automatically // on COMMIT or ROLLBACK, so no manual unlock is needed even if the transaction fails partway. const bootstrapLockNamespace = 1_500_000_001; const bootstrapLockKey = 1_500_000_002; export interface BootstrapPlatformOwnerInput { readonly userId: EkklesiaUserId; } /** * Core's dedicated, Hono-independent platform-owner bootstrap operation. Deliberately not a * composition of "check, then call the existing grant function": the single-use claim, the target * user's existence check, the owner-grant insert, and its audit entry are all one PostgreSQL * transaction, serialized by a transaction-scoped advisory lock acquired as the very first * statement — so two concurrent callers can never both observe "not yet consumed" and both insert. * The second caller blocks on the lock until the first's transaction commits or rolls back, then * re-evaluates against the now-current state. * * **Single-use authority lives in `ekklesia_core_platform_owner_bootstrap`, never in the continued * existence of a platform-owner row.** The owner table's `user_id` cascades on user deletion, so * treating "an owner row exists" as the consumed marker meant deleting the bootstrapped owner's * user row silently reopened the one operation that establishes a platform's first administrator — * along with the audit evidence that it had ever run. The dedicated singleton has no user foreign * key, is never deleted by Core, and therefore survives owner revocation and user deletion alike. * * Consumption is deliberately tied to *success*: the sentinel is written inside the same * transaction as the owner row and its audit entry, so a missing target user, a constraint * violation, or any other failure rolls the sentinel back with everything else and leaves the one * legitimate first use still available. There is no reset command, deletion path, HTTP endpoint, * environment override, or force flag — by design, "consumed" is permanent. */ export async function bootstrapPlatformOwner( db: AuthorizationDrizzleDatabase, input: BootstrapPlatformOwnerInput, ): Promise { try { await db.transaction(async (tx) => { await tx.execute( sql`select pg_advisory_xact_lock(${bootstrapLockNamespace}, ${bootstrapLockKey})`, ); const consumedRows = await tx .select({ id: ekklesiaCorePlatformOwnerBootstrap.id }) .from(ekklesiaCorePlatformOwnerBootstrap) .limit(1); if (consumedRows.length > 0) { throw new PlatformOwnerBootstrapError("PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED"); } const targetUserRows = await tx .select({ id: ekklesiaCoreUser.id }) .from(ekklesiaCoreUser) .where(eq(ekklesiaCoreUser.id, input.userId)) .limit(1); if (targetUserRows.length === 0) { throw new PlatformOwnerBootstrapError("PLATFORM_OWNER_BOOTSTRAP_USER_NOT_FOUND"); } // The sentinel is claimed inside the same transaction as everything below it. The advisory // lock already serializes concurrent callers; the singleton primary key is the independent, // structural backstop that makes a second row impossible even if a future caller reached // this insert without the lock. await tx.insert(ekklesiaCorePlatformOwnerBootstrap).values({ consumedForUserId: input.userId, id: PLATFORM_OWNER_BOOTSTRAP_SINGLETON_ID, }); await tx.insert(ekklesiaCorePlatformOwner).values({ grantedByActorId: BOOTSTRAP_ACTOR_ID, grantedByActorType: BOOTSTRAP_ACTOR_TYPE, userId: input.userId, }); await tx.insert(ekklesiaCoreAuthorizationAudit).values({ action: "platform_owner.grant", actorId: BOOTSTRAP_ACTOR_ID, actorType: BOOTSTRAP_ACTOR_TYPE, subjectUserId: input.userId, }); }); } catch (error) { if (isPlatformOwnerBootstrapError(error)) { throw error; } throw new PlatformOwnerBootstrapError("PLATFORM_OWNER_BOOTSTRAP_FAILED"); } }