import type { ApiCredentialRepository } from '@qinglong/runtime-core/api-credential'; import type { LocalOwnerPepperRepository } from '@qinglong/runtime-core/local-owner-pepper'; import type { ProjectPolicyRepository } from '@qinglong/runtime-core/project-policy'; import type { RunManualRetryRepository } from '@qinglong/runtime-core/run-manual-retry'; import type { SecurityAuditSink } from '@qinglong/runtime-core/security-audit'; import { LocalSqliteOperationAuthority } from '../authority/operationAuthority'; import { LocalSqliteOwnerPepperRepository } from '../local-owner/ownerPepperRepository'; import { EDGE_RUN_MANUAL_RETRY_RATE_LIMIT, LocalSqliteRunManualRetryRepository, STANDALONE_RUN_MANUAL_RETRY_RATE_LIMIT, } from '../run/runManualRetryRepository'; import { LocalSqliteRunCancellationRepository } from '../run/runCancellationRepository'; import { LocalSqliteApiCredentialRepository } from '../security/apiCredentialRepository'; import { LocalSqliteProjectPolicyRepository } from '../security/projectPolicyRepository'; import { LocalSqliteSecurityAuthorityStore } from '../security/securityAuthorityStore'; import { assertLocalSqliteOptions, assertLocalSqlitePathBoundary, openLocalSqliteClient, type LocalSqliteDatabaseOptions, type LocalSqliteProfile, } from '../storage/config'; import { auditLocalSqliteReadiness, type LocalSqliteReadinessEvidence, } from '../readiness/readiness'; import { confirmLocalSqliteAuthenticatedUserCredentialFence, LocalSqliteAuthenticatedManagementFenceError, type LocalSqliteAuthenticatedUserCredentialFence, } from './packageManagement'; export interface LocalSqliteRunManagementDatabase { readonly profile: LocalSqliteProfile; readonly readiness: LocalSqliteReadinessEvidence; readonly apiCredentials: ApiCredentialRepository; readonly ownerPepper: Pick; readonly projectPolicy: ProjectPolicyRepository; readonly runCancellation: Pick< LocalSqliteRunCancellationRepository, 'requestUserCancellationAudited' >; readonly runManualRetry: RunManualRetryRepository; readonly securityAudit: SecurityAuditSink; activateUserCredentialFence( fence: Readonly, ): void; close(): Promise; } function sameCredentialFence( left: Readonly, right: Readonly, ): boolean { return ( left.credentialId === right.credentialId && left.credentialVersion === right.credentialVersion && left.pepperKeyId === right.pepperKeyId && left.materialDigest === right.materialDigest && left.subjectType === right.subjectType && left.subjectId === right.subjectId && left.secretDigest === right.secretDigest && left.notBeforeAtMs === right.notBeforeAtMs && left.expiresAtMs === right.expiresAtMs ); } /** Short-lived strong-User authority; it never migrates or starts a timer. */ export async function openLocalSqliteRunManagementDatabase( options: LocalSqliteDatabaseOptions, ): Promise { assertLocalSqliteOptions(options); assertLocalSqlitePathBoundary(options.databasePath, false); const client = openLocalSqliteClient(options, false); try { const readiness = await auditLocalSqliteReadiness(client); const authority = new LocalSqliteOperationAuthority(client); let activeFence: | Readonly | undefined; const securityAuthority = new LocalSqliteSecurityAuthorityStore(authority); const runManualRetry = new LocalSqliteRunManualRetryRepository(authority, { rateLimit: options.profile === 'edge' ? EDGE_RUN_MANUAL_RETRY_RATE_LIMIT : STANDALONE_RUN_MANUAL_RETRY_RATE_LIMIT, beforeMutation(actor) { if ( !activeFence || actor.type !== activeFence.subjectType || actor.id !== activeFence.subjectId ) { throw new LocalSqliteAuthenticatedManagementFenceError(); } confirmLocalSqliteAuthenticatedUserCredentialFence( authority, activeFence, ); }, }); const runCancellation = new LocalSqliteRunCancellationRepository( authority, Date.now, { beforeMutation(actor) { if ( !activeFence || actor.type !== activeFence.subjectType || actor.id !== activeFence.subjectId ) { throw new LocalSqliteAuthenticatedManagementFenceError(); } confirmLocalSqliteAuthenticatedUserCredentialFence( authority, activeFence, ); }, }, ); let closePromise: Promise | undefined; return Object.freeze({ profile: options.profile, readiness, apiCredentials: new LocalSqliteApiCredentialRepository(authority), ownerPepper: new LocalSqliteOwnerPepperRepository(authority), projectPolicy: new LocalSqliteProjectPolicyRepository(authority), runCancellation, runManualRetry, securityAudit: securityAuthority, activateUserCredentialFence( fence: Readonly, ) { confirmLocalSqliteAuthenticatedUserCredentialFence(authority, fence); if (activeFence && !sameCredentialFence(activeFence, fence)) { throw new LocalSqliteAuthenticatedManagementFenceError(); } activeFence = Object.freeze({ ...fence }); }, close() { if (closePromise) return closePromise; closePromise = authority.close(); return closePromise; }, }); } catch (error) { if (client.isOpen) client.close(); throw error; } } export type { LocalSqliteDatabaseOptions, LocalSqliteProfile }; export type { LocalSqliteReadinessEvidence } from '../readiness/readiness';