import { getUserById, OWNER_USER_ID } from 'officerdb'; // Identifies the platform owner. The user's `role` column IS the answer — there is no second mechanism // and no environment variable. // // This used to resolve the owner two other ways: SUPER_ADMIN_EMAIL from .env, falling back to the // lowest user id. Both are gone. Three sources of truth for "who owns this server" (those two plus the // role column) disagree the moment one is edited, and the failure is silent — an account quietly gains // or loses the vault, the platform origin, and the identity the agent sidecar runs as. // // NOT CACHED, deliberately. The previous version cached an owner id and justified it with "the owner // never changes at runtime (bootstrap is closed after user #1)". That stops being true the moment roles // are editable, and a cache with no invalidation contract is a staleness bug waiting for whoever builds // the role UI. This is one primary-key lookup on a request that has already verified a JWT. export async function isSuperAdmin(payload: { id?: number } | null | undefined): Promise { return isOwner(payload); } /** * The owner is an IDENTITY, not a role. `OWNER_USER_ID` and nothing else. * * This asked `user.role === 'Super Admin'` until 2026-08-23, which was correct while roles were a hardcoded * TypeScript union and nothing could write a new one. The moment roles became a table it became a * privilege-escalation path: "add a role" and "become the owner" are the same feature, reachable from * whatever UI manages roles. * * So the question moved to one nothing in the product can answer for itself. `Super Admin` is still a role * and several accounts may hold it — it provisions an OS account with sudo, which is its own decision — but * holding it is not being the owner and no role grants that. * * Kept as a database read rather than a bare comparison, deliberately. A deleted or blocked account must not * still be the owner because its id happens to match, and this is one primary-key lookup on a request that * has already verified a JWT. */ export async function isOwner(payload: { id?: number } | null | undefined): Promise { if (payload?.id !== OWNER_USER_ID) return false; try { const user = await getUserById(OWNER_USER_ID); return !!user; } catch { // A transient database error must never promote anyone. Deny, and let the next call retry. return false; } }