import { randomUUID } from "node:crypto"; import type { Prisma, PrismaClient } from "@prisma/client"; import { fingerprintFeishuAppId, validateFeishuApplicationCredential, type FeishuApplicationCredentialInput, } from "../connections/feishuApplicationConnections.js"; import { decryptStoredProviderCredential, ProviderConnectionService, type ProviderCredentialInput, } from "../connections/providerConnections.js"; import { probeFeishuApplication, type FeishuReadinessProbe } from "../connections/feishuReadiness.js"; import { probeOpenRouterCredential, type ProviderReadinessProbe } from "../connections/providerReadiness.js"; import { deterministicFeishuUserId, scopedFeishuPrincipalId } from "../feishu/identityNamespace.js"; import { LocalSecretEnvelope } from "../security/secretEnvelope.js"; export interface AlphaSiloBootstrapInput { readonly organization: { readonly id: string; readonly slug: string; readonly name: string }; readonly owner: { readonly openId: string; readonly displayName: string; readonly unionId?: string }; readonly feishu: FeishuApplicationCredentialInput; readonly provider: ProviderCredentialInput & { readonly providerId: string }; readonly teams?: readonly { readonly slug: string; readonly name: string; readonly description?: string; }[]; } export interface AlphaSiloBootstrapResult { readonly organizationId: string; readonly ownerUserId: string; readonly feishuConnectionId: string; readonly initialized: boolean; readonly providerConfigured: boolean; } /** * Idempotently bootstrap the one Organization allowed in an Alpha Silo. * Secrets are accepted only in-memory and persisted as authenticated envelopes. */ export async function bootstrapAlphaSilo( prisma: PrismaClient, secrets: LocalSecretEnvelope, input: AlphaSiloBootstrapInput, probes: { readonly feishu?: FeishuReadinessProbe; readonly provider?: ProviderReadinessProbe; } = {}, ): Promise { const normalized = validateInput(input); await removePristineLegacyMigrationOrganization(prisma); const existing = await loadExistingSilo(prisma, normalized.organization.id, normalized.owner.openId); let initialized = false; let ownerUserId: string; let feishuConnectionId: string; if (existing === null) { await (probes.feishu ?? probeFeishuApplication)(normalized.feishu); const initializedSilo = await initializeSilo(prisma, secrets, normalized); initialized = true; ownerUserId = initializedSilo.ownerUserId; feishuConnectionId = initializedSilo.feishuConnectionId; } else { ownerUserId = existing.ownerUserId; feishuConnectionId = existing.feishuConnectionId; const expectedFingerprint = fingerprintFeishuAppId(normalized.feishu.appId); if (existing.appIdentityFingerprint !== expectedFingerprint) { throw new Error("bootstrap Feishu app identity does not match the existing Silo"); } } const providerService = new ProviderConnectionService( prisma, secrets, probes.provider ?? probeOpenRouterCredential, ); const existingProviders = await providerService.list(normalized.organization.id); const existingProvider = existingProviders.find((connection) => connection.providerId === normalized.provider.providerId); if (existingProvider === undefined) { await providerService.rotateByok({ organizationId: normalized.organization.id, actorUserId: ownerUserId, providerId: normalized.provider.providerId, baseUrl: normalized.provider.baseUrl, authToken: normalized.provider.authToken, ...(normalized.provider.anthropicApiKey !== undefined ? { anthropicApiKey: normalized.provider.anthropicApiKey } : {}), }); } else if (existingProvider.status !== "ACTIVE" || existingProvider.mode !== "BYOK") { throw new Error(`bootstrap provider ${normalized.provider.providerId} exists but is not ACTIVE BYOK`); } else { const stored = await loadExistingProviderCredential( prisma, secrets, normalized.organization.id, normalized.provider.providerId, ); if (stored.baseUrl !== normalized.provider.baseUrl || stored.authToken !== normalized.provider.authToken || stored.anthropicApiKey !== (normalized.provider.anthropicApiKey ?? "")) { throw new Error(`bootstrap provider ${normalized.provider.providerId} credential does not match existing Silo`); } } await ensureBootstrapTeams(prisma, normalized.organization.id, ownerUserId, normalized.teams ?? []); return { organizationId: normalized.organization.id, ownerUserId, feishuConnectionId, initialized, providerConfigured: true, }; } const LEGACY_MIGRATION_ORGANIZATION = { id: "org_default", slug: "legacy-default", name: "Legacy Default Organization", inboxId: "folder_inbox_2b99350e0db97ad0cbcb55c20ee8bafa", } as const; /** * A fresh database still receives the compatibility Organization inserted by * the tenant-root migration, plus its later default settings and Inbox. An * Alpha Silo bootstrap may replace only that exact, otherwise-unused scaffold. * Any tenant data or shape drift remains a hard manual-repair error. */ async function removePristineLegacyMigrationOrganization(prisma: PrismaClient): Promise { await prisma.$transaction(async (tx) => { const organization = await tx.organization.findUnique({ where: { id: LEGACY_MIGRATION_ORGANIZATION.id }, include: { memberships: { take: 1, select: { id: true } }, projects: { take: 1, select: { id: true } }, teams: { take: 1, select: { id: true } }, externalDirectoryConnections: { take: 1, select: { id: true } }, providerConnections: { take: 1, select: { id: true } }, feishuApplicationConnection: { select: { id: true } }, auditEntries: { take: 1, select: { id: true } }, projectSettings: true, folders: { take: 2, select: { id: true, parentId: true, name: true, sortKey: true, archivedAt: true }, }, }, }); if (organization === null) return; const pristine = organization.slug === LEGACY_MIGRATION_ORGANIZATION.slug && organization.name === LEGACY_MIGRATION_ORGANIZATION.name && organization.status === "ACTIVE" && organization.memberships.length === 0 && organization.projects.length === 0 && organization.teams.length === 0 && organization.externalDirectoryConnections.length === 0 && organization.providerConnections.length === 0 && organization.feishuApplicationConnection === null && organization.auditEntries.length === 0 && organization.projectSettings?.membersCanCreateProjects === true && organization.folders.length === 1 && organization.folders[0]?.id === LEGACY_MIGRATION_ORGANIZATION.inboxId && organization.folders[0].parentId === null && organization.folders[0].name === "Inbox" && organization.folders[0].sortKey === "000000" && organization.folders[0].archivedAt === null; if (pristine) { await tx.organization.delete({ where: { id: organization.id } }); } }); } async function ensureBootstrapTeams( prisma: PrismaClient, organizationId: string, ownerUserId: string, teams: NonNullable, ): Promise { await prisma.$transaction(async (tx) => { for (const team of teams) { const existing = await tx.team.findFirst({ where: { organizationId, slug: team.slug, archivedAt: null }, select: { id: true, name: true, description: true }, }); if (existing !== null && (existing.name !== team.name || existing.description !== (team.description ?? null))) { throw new Error(`bootstrap Team ${team.slug} does not match the existing Silo`); } const teamId = existing?.id ?? (await tx.team.create({ data: { organizationId, slug: team.slug, name: team.name, ...(team.description !== undefined ? { description: team.description } : {}), }, select: { id: true }, })).id; const membership = await tx.teamMembership.findFirst({ where: { teamId, userId: ownerUserId, revokedAt: null }, select: { id: true }, }); if (membership === null) await tx.teamMembership.create({ data: { teamId, userId: ownerUserId } }); } }); } async function initializeSilo( prisma: PrismaClient, secrets: LocalSecretEnvelope, input: AlphaSiloBootstrapInput, ): Promise<{ readonly ownerUserId: string; readonly feishuConnectionId: string }> { const connectionId = randomUUID(); const secretVersionId = randomUUID(); const identityId = randomUUID(); const ownerUserId = deterministicFeishuUserId(connectionId, input.owner.openId); const appIdentityFingerprint = fingerprintFeishuAppId(input.feishu.appId); const payload = validateFeishuApplicationCredential(input.feishu); const envelope = secrets.encryptJson({ purpose: "feishu-application", organizationId: input.organization.id, connectionId, secretVersionId, }, payload); await prisma.$transaction(async (tx) => { const count = await tx.organization.count(); if (count !== 0) throw new Error(`Silo bootstrap requires an empty Organization set; found ${count}`); await tx.organization.create({ data: { ...input.organization } }); await tx.organizationAgentRole.createMany({ data: [ { organizationId: input.organization.id, roleId: "draft", label: "草稿", sortOrder: 10, isDefault: true, }, { organizationId: input.organization.id, roleId: "review", label: "审校", sortOrder: 20, }, ], }); await tx.organizationProjectSettings.create({ data: { organizationId: input.organization.id, membersCanCreateProjects: true }, }); await tx.folder.create({ data: { organizationId: input.organization.id, name: "Inbox", sortKey: "000000", }, }); await tx.user.create({ data: { id: ownerUserId, feishuOpenId: scopedFeishuPrincipalId("USER", connectionId, input.owner.openId), displayName: input.owner.displayName, }, }); await tx.organizationFeishuApplicationConnection.create({ data: { id: connectionId, organizationId: input.organization.id, appIdentityFingerprint, status: "DRAFT", }, }); await tx.feishuUserIdentity.create({ data: { id: identityId, connectionId, userId: ownerUserId, openId: input.owner.openId, ...(input.owner.unionId !== undefined ? { unionId: input.owner.unionId } : {}), }, }); await tx.organizationMembership.create({ data: { organizationId: input.organization.id, userId: ownerUserId, role: "OWNER" }, }); for (const team of input.teams ?? []) { await tx.team.create({ data: { organizationId: input.organization.id, slug: team.slug, name: team.name, ...(team.description !== undefined ? { description: team.description } : {}), memberships: { create: { userId: ownerUserId } }, }, }); } await tx.feishuApplicationCredentialVersion.create({ data: { id: secretVersionId, connectionId, version: 1, envelopeVersion: envelope.version, keyId: envelope.keyId, envelope: envelope as unknown as Prisma.InputJsonValue, createdByUserId: ownerUserId, }, }); await tx.organizationFeishuApplicationConnection.update({ where: { id: connectionId }, data: { status: "ACTIVE", activeSecretVersionId: secretVersionId, activatedAt: new Date(), }, }); await tx.auditEntry.create({ data: { organizationId: input.organization.id, actorUserId: ownerUserId, action: "alpha_silo.bootstrapped", metadata: { connectionId, ownerIdentityId: identityId, envelopeVersion: envelope.version, keyId: envelope.keyId, }, }, }); }); return { ownerUserId, feishuConnectionId: connectionId }; } async function loadExistingSilo( prisma: PrismaClient, organizationId: string, ownerOpenId: string, ): Promise<{ readonly ownerUserId: string; readonly feishuConnectionId: string; readonly appIdentityFingerprint: string; } | null> { const organizations = await prisma.organization.findMany({ take: 2, include: { memberships: { where: { role: "OWNER", revokedAt: null }, take: 2, include: { user: { include: { feishuIdentities: true } } }, }, feishuApplicationConnection: true, }, }); if (organizations.length === 0) return null; if (organizations.length !== 1 || organizations[0]!.id !== organizationId) { throw new Error("bootstrap database is not the configured single-Organization Silo"); } const organization = organizations[0]!; if (organization.status !== "ACTIVE" || organization.memberships.length !== 1 || organization.feishuApplicationConnection?.status !== "ACTIVE") { throw new Error("existing Silo is incomplete or inactive; manual repair is required"); } const ownerIdentity = organization.memberships[0]!.user.feishuIdentities.find( (identity) => identity.connectionId === organization.feishuApplicationConnection!.id, ); if (ownerIdentity?.openId !== ownerOpenId) { throw new Error("bootstrap owner identity does not match the existing Silo"); } return { ownerUserId: organization.memberships[0]!.userId, feishuConnectionId: organization.feishuApplicationConnection.id, appIdentityFingerprint: organization.feishuApplicationConnection.appIdentityFingerprint, }; } async function loadExistingProviderCredential( prisma: PrismaClient, secrets: LocalSecretEnvelope, organizationId: string, providerId: string, ): Promise<{ readonly baseUrl: string; readonly authToken: string; readonly anthropicApiKey: string }> { const connection = await prisma.organizationProviderConnection.findUnique({ where: { organizationId_providerId: { organizationId, providerId } }, include: { activeSecretVersion: true }, }); const version = connection?.activeSecretVersion; if (connection === null || connection === undefined || version === null || version === undefined || connection.status !== "ACTIVE" || version.retiredAt !== null) { throw new Error(`active provider connection not found: ${providerId}`); } return decryptStoredProviderCredential(secrets, { organizationId, connectionId: connection.id, providerId, secretVersionId: version.id, envelopeVersion: version.envelopeVersion, keyId: version.keyId, envelope: version.envelope, }); } function validateInput(input: AlphaSiloBootstrapInput): AlphaSiloBootstrapInput { const required = (value: string, label: string): string => { const normalized = value.trim(); if (normalized === "") throw new Error(`${label} is required`); return normalized; }; const slug = required(input.organization.slug, "Organization slug"); if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(slug)) { throw new Error("Organization slug must be lowercase alphanumeric with optional hyphens"); } if (input.provider.providerId.trim() !== "openrouter") { throw new Error("Alpha Silo currently requires providerId=openrouter"); } return { organization: { id: required(input.organization.id, "Organization id"), slug, name: required(input.organization.name, "Organization name"), }, owner: { openId: required(input.owner.openId, "Owner Feishu openId"), displayName: required(input.owner.displayName, "Owner displayName"), ...(input.owner.unionId !== undefined ? { unionId: required(input.owner.unionId, "Owner unionId") } : {}), }, feishu: input.feishu, provider: input.provider, ...(input.teams !== undefined ? { teams: input.teams.map((team) => { const teamSlug = required(team.slug, "Team slug"); if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(teamSlug)) { throw new Error(`invalid Team slug: ${teamSlug}`); } return { slug: teamSlug, name: required(team.name, `Team ${teamSlug} name`), ...(team.description !== undefined ? { description: team.description.trim() } : {}), }; }), } : {}), }; }