import * as plugins from './plugins.js'; import { controllerMcpTypedRequestPath, controllerPackageName, controllerProtocolVersion, controllerUpgradeManagementVersion, } from '../ts_interfaces/index.js'; import { commitinfo } from './00_commitinfo_data.js'; import { resolveAGLHomePaths, type IAGLHomePaths, } from './classes.aglhome.js'; import { readControllerProcessIdentity, type IControllerProcessIdentity, } from './classes.processinspection.js'; const controllerMcpDescriptorSchemaVersion = 1 as const; const controllerMcpDescriptorMaximumBytes = 16 * 1024; const controllerMcpDescriptorMaximumDirectoryEntries = 256; const lifecycleGenerationPattern = /^[A-Za-z0-9_-]{22,128}$/; const tokenPattern = /^[A-Za-z0-9_-]{43}$/; const startTicksPattern = /^[1-9][0-9]*$/; export type TControllerMcpDescriptorErrorCode = 'CONTROLLER_UNAVAILABLE' | 'FENCED'; export class ControllerMcpDescriptorError extends Error { public readonly code: TControllerMcpDescriptorErrorCode; constructor( codeArg: TControllerMcpDescriptorErrorCode, messageArg: string, optionsArg?: ErrorOptions, ) { super(messageArg, optionsArg); this.name = 'ControllerMcpDescriptorError'; this.code = codeArg; } } export interface IControllerMcpDescriptor { schemaVersion: typeof controllerMcpDescriptorSchemaVersion; packageName: string; packageVersion: string; protocolVersion: number; upgradeManagementVersion: number; controllerPort: number; lifecycleGeneration: string; origin: string; privatePort: number; path: typeof controllerMcpTypedRequestPath; controllerPid: number; processGroupId: number; processFingerprint: string; processStartTicks: string; token: string; publishedAt: number; } export interface IControllerMcpDescriptorFileIdentity { device: string; inode: string; } export interface IControllerMcpDescriptorRecord { descriptor: IControllerMcpDescriptor; fileIdentity: IControllerMcpDescriptorFileIdentity; } interface IControllerMcpDescriptorCleanupState { temporaryPath?: string; destinationPath: string; temporaryIdentity?: IControllerMcpDescriptorFileIdentity; temporaryHandle?: plugins.fs.promises.FileHandle; retainedHandles: Set; temporaryMayExist: boolean; destinationMayExist: boolean; directoryNeedsSync: boolean; expectedDestination?: IControllerMcpDescriptorRecord; publishedRecordToClear?: IControllerMcpDescriptorRecord; fenceTemporaryReplacement?: boolean; expectedTemporaryLinkCount?: bigint; temporaryRequiresDestinationLink?: boolean; } export interface IControllerMcpDescriptorAccessOptions { paths?: IAGLHomePaths; readProcessIdentity?: ( pidArg: number, ) => Promise; } export interface IControllerMcpDescriptorPublisherOptions extends IControllerMcpDescriptorAccessOptions { controllerPort: number; } export interface IPublishControllerMcpDescriptorInput { lifecycleGeneration: string; privatePort: number; processIdentity: IControllerProcessIdentity; } const descriptorKeys = [ 'schemaVersion', 'packageName', 'packageVersion', 'protocolVersion', 'upgradeManagementVersion', 'controllerPort', 'lifecycleGeneration', 'origin', 'privatePort', 'path', 'controllerPid', 'processGroupId', 'processFingerprint', 'processStartTicks', 'token', 'publishedAt', ] as const; const unavailable = (causeArg?: unknown): ControllerMcpDescriptorError => new ControllerMcpDescriptorError( 'CONTROLLER_UNAVAILABLE', 'The requested AGL controller is not available.', causeArg === undefined ? undefined : { cause: causeArg }, ); const fenced = (causeArg?: unknown): ControllerMcpDescriptorError => new ControllerMcpDescriptorError( 'FENCED', 'The private AGL controller endpoint is fenced by unsafe runtime state.', causeArg === undefined ? undefined : { cause: causeArg }, ); const isMissingError = (errorArg: unknown): boolean => ( (errorArg as NodeJS.ErrnoException).code === 'ENOENT' ); const currentUid = (): number => { if (process.platform !== 'linux' || typeof process.getuid !== 'function') throw fenced(); return process.getuid(); }; const assertPort = (portArg: number): number => { if (!Number.isSafeInteger(portArg) || portArg < 1 || portArg > 65_535) throw fenced(); return portArg; }; const identityFromStats = ( statsArg: plugins.fs.BigIntStats, ): IControllerMcpDescriptorFileIdentity => ({ device: statsArg.dev.toString(10), inode: statsArg.ino.toString(10), }); const identitiesEqual = ( leftArg: IControllerMcpDescriptorFileIdentity, rightArg: IControllerMcpDescriptorFileIdentity, ): boolean => leftArg.device === rightArg.device && leftArg.inode === rightArg.inode; const assertExactKeys = (valueArg: Record): void => { const keys = Object.keys(valueArg); if ( keys.length !== descriptorKeys.length || keys.some((keyArg, indexArg) => keyArg !== descriptorKeys[indexArg]) ) throw fenced(); }; const assertOwnedPrivateDirectory = async (directoryArg: string): Promise => { let stats: plugins.fs.BigIntStats; let canonical: string; try { [stats, canonical] = await Promise.all([ plugins.fs.promises.lstat(directoryArg, { bigint: true }), plugins.fs.promises.realpath(directoryArg), ]); } catch (errorArg) { if (isMissingError(errorArg)) throw unavailable(); throw fenced(); } if ( canonical !== directoryArg || stats.isSymbolicLink() || !stats.isDirectory() || stats.uid !== BigInt(currentUid()) || Number(stats.mode & 0o777n) !== 0o700 ) throw fenced(); }; const lstatIfPresent = async ( pathArg: string, ): Promise => { try { return await plugins.fs.promises.lstat(pathArg, { bigint: true }); } catch (errorArg) { if (isMissingError(errorArg)) return undefined; throw errorArg; } }; const assertOwnedDescriptorStats = (statsArg: plugins.fs.BigIntStats): void => { if ( statsArg.isSymbolicLink() || !statsArg.isFile() || statsArg.uid !== BigInt(currentUid()) || Number(statsArg.mode & 0o777n) !== 0o600 ) throw fenced(); }; const assertSecureDescriptorStats = ( statsArg: plugins.fs.BigIntStats, optionsArg: { linkCount?: bigint; exactSize?: number; allowEmpty?: boolean; } = {}, ): void => { assertOwnedDescriptorStats(statsArg); if ( statsArg.nlink !== (optionsArg.linkCount ?? 1n) || statsArg.size > BigInt(controllerMcpDescriptorMaximumBytes) || (optionsArg.allowEmpty !== true && statsArg.size < 2n) || (optionsArg.exactSize !== undefined && statsArg.size !== BigInt(optionsArg.exactSize)) ) throw fenced(); }; const descriptorError = (errorArg: unknown): ControllerMcpDescriptorError => ( errorArg instanceof ControllerMcpDescriptorError ? errorArg : fenced(errorArg) ); const maximumDescriptorCleanupAttempts = 2; const closeFileHandle = async (handleArg: plugins.fs.promises.FileHandle): Promise => { const errors: unknown[] = []; for (let attempt = 0; attempt < maximumDescriptorCleanupAttempts; attempt += 1) { try { await handleArg.close(); return; } catch (errorArg) { errors.push(errorArg); } } throw new AggregateError(errors, 'Descriptor file handle cleanup failed.'); }; const descriptorPathFor = (pathsArg: IAGLHomePaths, controllerPortArg: number): string => ( plugins.path.join(pathsArg.mcpRuntime, `controller-${assertPort(controllerPortArg)}.json`) ); const parseDescriptor = ( textArg: string, expectedControllerPortArg: number, ): IControllerMcpDescriptor => { let raw: unknown; try { raw = JSON.parse(textArg); } catch { throw fenced(); } if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw fenced(); const value = raw as Record; assertExactKeys(value); const controllerPort = assertPort(value.controllerPort as number); const privatePort = assertPort(value.privatePort as number); if ( value.schemaVersion !== controllerMcpDescriptorSchemaVersion || typeof value.packageName !== 'string' || value.packageName.length < 1 || value.packageName.length > 128 || typeof value.packageVersion !== 'string' || value.packageVersion.length < 1 || value.packageVersion.length > 128 || !Number.isSafeInteger(value.protocolVersion) || (value.protocolVersion as number) < 1 || !Number.isSafeInteger(value.upgradeManagementVersion) || (value.upgradeManagementVersion as number) < 1 || controllerPort !== expectedControllerPortArg || typeof value.lifecycleGeneration !== 'string' || !lifecycleGenerationPattern.test(value.lifecycleGeneration) || value.origin !== `http://127.0.0.1:${privatePort}` || value.path !== controllerMcpTypedRequestPath || !Number.isSafeInteger(value.controllerPid) || (value.controllerPid as number) < 1 || !Number.isSafeInteger(value.processGroupId) || (value.processGroupId as number) < 1 || typeof value.processStartTicks !== 'string' || !startTicksPattern.test(value.processStartTicks) || value.processFingerprint !== `linux:${value.controllerPid}:${value.processStartTicks}` || typeof value.token !== 'string' || !tokenPattern.test(value.token) || Buffer.from(value.token, 'base64url').byteLength !== 32 || !Number.isSafeInteger(value.publishedAt) || (value.publishedAt as number) < 1 ) throw fenced(); const descriptor = value as unknown as IControllerMcpDescriptor; if (serializeControllerMcpDescriptor(descriptor) !== textArg) throw fenced(); return descriptor; }; export const serializeControllerMcpDescriptor = ( descriptorArg: IControllerMcpDescriptor, ): string => `${JSON.stringify(descriptorArg)}\n`; const readBoundedFile = async ( handleArg: plugins.fs.promises.FileHandle, ): Promise<{ bytes: Buffer; stats: plugins.fs.BigIntStats }> => { const before = await handleArg.stat({ bigint: true }); assertSecureDescriptorStats(before); const bytes = Buffer.alloc(Number(before.size) + 1); let offset = 0; while (offset < bytes.length) { const read = await handleArg.read(bytes, offset, bytes.length - offset, null); if (read.bytesRead === 0) break; offset += read.bytesRead; } const after = await handleArg.stat({ bigint: true }); assertSecureDescriptorStats(after); if ( offset !== Number(before.size) || after.size !== before.size || after.dev !== before.dev || after.ino !== before.ino || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs ) throw fenced(); return { bytes: bytes.subarray(0, offset), stats: after }; }; const readSecureDescriptor = async ( controllerPortArg: number, pathsArg: IAGLHomePaths, ): Promise => { await assertOwnedPrivateDirectory(pathsArg.mcpRuntime); const descriptorPath = descriptorPathFor(pathsArg, controllerPortArg); let initial: plugins.fs.BigIntStats; try { initial = await plugins.fs.promises.lstat(descriptorPath, { bigint: true }); } catch (errorArg) { if (isMissingError(errorArg)) throw unavailable(); throw fenced(); } assertSecureDescriptorStats(initial); let handle: plugins.fs.promises.FileHandle; try { handle = await plugins.fs.promises.open( descriptorPath, plugins.fs.constants.O_RDONLY | plugins.fs.constants.O_NOFOLLOW, ); } catch { throw fenced(); } let result: IControllerMcpDescriptorRecord | undefined; let operationError: unknown; try { const read = await readBoundedFile(handle); if (!identitiesEqual(identityFromStats(initial), identityFromStats(read.stats))) throw fenced(); const text = read.bytes.toString('utf8'); if (!Buffer.from(text, 'utf8').equals(read.bytes)) throw fenced(); result = { descriptor: parseDescriptor(text, controllerPortArg), fileIdentity: identityFromStats(read.stats), }; } catch (errorArg) { operationError = errorArg; } let closeError: unknown; try { await closeFileHandle(handle); } catch (errorArg) { closeError = errorArg; } if (operationError !== undefined && closeError !== undefined) { const combined = new AggregateError( [operationError, closeError], 'Descriptor read and file handle cleanup failed.', ); throw operationError instanceof ControllerMcpDescriptorError ? new ControllerMcpDescriptorError(operationError.code, operationError.message, { cause: combined, }) : fenced(combined); } if (operationError !== undefined) throw operationError; if (closeError !== undefined) throw fenced(closeError); if (!result) throw fenced(); return result; }; const descriptorProcessIsLive = async ( descriptorArg: IControllerMcpDescriptor, readProcessIdentityArg: ( pidArg: number, ) => Promise, ): Promise => { let identity: IControllerProcessIdentity | null; try { identity = await readProcessIdentityArg(descriptorArg.controllerPid); } catch (errorArg) { throw fenced(errorArg); } if (!identity) return false; return identity.processGroupId === descriptorArg.processGroupId && identity.fingerprint === descriptorArg.processFingerprint && identity.startTicks === descriptorArg.processStartTicks; }; const syncDirectory = async ( directoryArg: string, cleanupStateArg?: IControllerMcpDescriptorCleanupState, ): Promise => { const handle = await plugins.fs.promises.open( directoryArg, plugins.fs.constants.O_RDONLY | plugins.fs.constants.O_DIRECTORY, ); cleanupStateArg?.retainedHandles.add(handle); let syncError: unknown; try { await handle.sync(); } catch (errorArg) { syncError = errorArg; } let closeError: unknown; try { await closeFileHandle(handle); cleanupStateArg?.retainedHandles.delete(handle); } catch (errorArg) { closeError = errorArg; } if (syncError !== undefined && closeError !== undefined) { throw new AggregateError( [syncError, closeError], 'Descriptor directory sync and file handle cleanup failed.', ); } if (syncError !== undefined) throw syncError; if (closeError !== undefined) throw closeError; }; const ensureDescriptorDirectory = async (pathsArg: IAGLHomePaths): Promise => { await assertOwnedPrivateDirectory(pathsArg.runtime); try { await plugins.fs.promises.mkdir(pathsArg.mcpRuntime, { mode: 0o700 }); await syncDirectory(pathsArg.runtime); } catch (errorArg) { if ((errorArg as NodeJS.ErrnoException).code !== 'EEXIST') throw fenced(errorArg); } await assertOwnedPrivateDirectory(pathsArg.mcpRuntime); }; export const readLiveControllerMcpDescriptor = async ( controllerPortArg: number, optionsArg: IControllerMcpDescriptorAccessOptions = {}, ): Promise => { const paths = optionsArg.paths ?? resolveAGLHomePaths(); let record: IControllerMcpDescriptorRecord; try { record = await readSecureDescriptor(controllerPortArg, paths); } catch (errorArg) { if (errorArg instanceof ControllerMcpDescriptorError) throw errorArg; throw fenced(errorArg); } const live = await descriptorProcessIsLive( record.descriptor, optionsArg.readProcessIdentity ?? readControllerProcessIdentity, ); if (!live) throw unavailable(); return record; }; export class ControllerMcpDescriptorPublisher { private readonly paths: IAGLHomePaths; private readonly readProcessIdentity: ( pidArg: number, ) => Promise; private readonly controllerPort: number; private published?: IControllerMcpDescriptorRecord; private pendingCleanup?: IControllerMcpDescriptorCleanupState; private lifecycleOperation: Promise = Promise.resolve(); constructor(optionsArg: IControllerMcpDescriptorPublisherOptions) { this.paths = optionsArg.paths ?? resolveAGLHomePaths(); this.readProcessIdentity = optionsArg.readProcessIdentity ?? readControllerProcessIdentity; this.controllerPort = assertPort(optionsArg.controllerPort); } public get descriptor(): IControllerMcpDescriptor | undefined { return this.published === undefined ? undefined : { ...this.published.descriptor }; } public publish( inputArg: IPublishControllerMcpDescriptorInput, ): Promise { return this.runLifecycleOperation(() => this.publishExclusive(inputArg)); } public removeOwned(): Promise { return this.runLifecycleOperation(async () => { await this.retryPendingCleanup(); const published = this.published; if (!published) return; await assertOwnedPrivateDirectory(this.paths.mcpRuntime); let current: IControllerMcpDescriptorRecord; try { current = await readSecureDescriptor(this.controllerPort, this.paths); } catch (errorArg) { if ( errorArg instanceof ControllerMcpDescriptorError && errorArg.code === 'CONTROLLER_UNAVAILABLE' ) { if (this.published === published) this.published = undefined; return; } throw errorArg; } this.assertExpectedRecord(current, published); this.pendingCleanup = this.createRemovalCleanupState(published, published); await this.retryPendingCleanup(); }); } private runLifecycleOperation(operationArg: () => Promise): Promise { const result = this.lifecycleOperation.then(operationArg, operationArg); this.lifecycleOperation = result.then(() => undefined, () => undefined); return result; } private createRemovalCleanupState( expectedDestinationArg: IControllerMcpDescriptorRecord, publishedRecordToClearArg?: IControllerMcpDescriptorRecord, ): IControllerMcpDescriptorCleanupState { return { destinationPath: descriptorPathFor(this.paths, this.controllerPort), temporaryIdentity: expectedDestinationArg.fileIdentity, retainedHandles: new Set(), temporaryMayExist: false, destinationMayExist: true, directoryNeedsSync: false, expectedDestination: expectedDestinationArg, ...(publishedRecordToClearArg ? { publishedRecordToClear: publishedRecordToClearArg } : {}), }; } private assertExpectedRecord( currentArg: IControllerMcpDescriptorRecord, expectedArg: IControllerMcpDescriptorRecord, ): void { if ( !identitiesEqual(currentArg.fileIdentity, expectedArg.fileIdentity) || serializeControllerMcpDescriptor(currentArg.descriptor) !== serializeControllerMcpDescriptor(expectedArg.descriptor) ) throw fenced(); } private async cleanupOwnedPath( stateArg: IControllerMcpDescriptorCleanupState, pathArg: string, destinationArg: boolean, ): Promise { const errors: unknown[] = []; for (let attempt = 0; attempt < maximumDescriptorCleanupAttempts; attempt += 1) { if (destinationArg && stateArg.expectedDestination) { let currentRecord: IControllerMcpDescriptorRecord; try { currentRecord = await readSecureDescriptor(this.controllerPort, this.paths); } catch (errorArg) { if ( errorArg instanceof ControllerMcpDescriptorError && errorArg.code === 'CONTROLLER_UNAVAILABLE' ) { stateArg.destinationMayExist = false; return; } errors.push(errorArg); continue; } this.assertExpectedRecord(currentRecord, stateArg.expectedDestination); } let current: plugins.fs.BigIntStats | undefined; try { current = await lstatIfPresent(pathArg); } catch (errorArg) { errors.push(errorArg); continue; } if (!current) { if (destinationArg) stateArg.destinationMayExist = false; else stateArg.temporaryMayExist = false; return; } if ( !stateArg.temporaryIdentity || !identitiesEqual(identityFromStats(current), stateArg.temporaryIdentity) ) { if (destinationArg && stateArg.expectedDestination) throw fenced(); if (!destinationArg && stateArg.fenceTemporaryReplacement) throw fenced(); if (destinationArg) stateArg.destinationMayExist = false; else stateArg.temporaryMayExist = false; return; } if ( !destinationArg && stateArg.expectedTemporaryLinkCount !== undefined && current.nlink !== stateArg.expectedTemporaryLinkCount ) throw fenced(); if (!destinationArg && stateArg.temporaryRequiresDestinationLink) { const destination = await lstatIfPresent(stateArg.destinationPath); if ( !destination || destination.nlink !== 2n || !identitiesEqual(identityFromStats(destination), stateArg.temporaryIdentity) ) throw fenced(); assertSecureDescriptorStats(destination, { linkCount: 2n, allowEmpty: true, }); } try { assertOwnedDescriptorStats(current); } catch (errorArg) { errors.push(errorArg); continue; } stateArg.directoryNeedsSync = true; try { await plugins.fs.promises.unlink(pathArg); if (destinationArg) stateArg.destinationMayExist = false; else stateArg.temporaryMayExist = false; return; } catch (errorArg) { errors.push(errorArg); } } throw new AggregateError(errors, `Descriptor path cleanup failed: ${pathArg}`); } private async retryPendingCleanup(): Promise { const state = this.pendingCleanup; if (!state) return; const errors: unknown[] = []; if (!state.temporaryIdentity && state.temporaryHandle) { try { const stats = await state.temporaryHandle.stat({ bigint: true }); assertOwnedDescriptorStats(stats); state.temporaryIdentity = identityFromStats(stats); } catch (errorArg) { errors.push(errorArg); } } for (const handle of [...state.retainedHandles]) { if (handle === state.temporaryHandle && !state.temporaryIdentity) continue; try { await closeFileHandle(handle); state.retainedHandles.delete(handle); if (state.temporaryHandle === handle) state.temporaryHandle = undefined; } catch (errorArg) { errors.push(errorArg); } } if (state.temporaryMayExist && state.temporaryPath) { try { await this.cleanupOwnedPath(state, state.temporaryPath, false); } catch (errorArg) { errors.push(errorArg); } } if (state.destinationMayExist) { try { await this.cleanupOwnedPath(state, state.destinationPath, true); } catch (errorArg) { errors.push(errorArg); } } if (state.directoryNeedsSync) { try { await syncDirectory(this.paths.mcpRuntime, state); state.directoryNeedsSync = false; } catch (errorArg) { errors.push(errorArg); } } const cleanupComplete = state.retainedHandles.size === 0 && !state.temporaryMayExist && !state.destinationMayExist && !state.directoryNeedsSync; if (errors.length > 0 || !cleanupComplete) { throw fenced(new AggregateError( errors.length > 0 ? errors : [new Error('Descriptor cleanup remained incomplete.')], 'Descriptor publication cleanup failed.', )); } if (this.pendingCleanup === state) this.pendingCleanup = undefined; if ( state.publishedRecordToClear && this.published === state.publishedRecordToClear ) this.published = undefined; } private async publishExclusive( inputArg: IPublishControllerMcpDescriptorInput, ): Promise { await this.retryPendingCleanup(); if (this.published) throw fenced(); if ( !lifecycleGenerationPattern.test(inputArg.lifecycleGeneration) || inputArg.processIdentity.pid !== process.pid || inputArg.processIdentity.processGroupId < 1 || inputArg.processIdentity.startTicks === undefined || !startTicksPattern.test(inputArg.processIdentity.startTicks) || inputArg.processIdentity.fingerprint !== `linux:${inputArg.processIdentity.pid}:${inputArg.processIdentity.startTicks}` ) throw fenced(); const privatePort = assertPort(inputArg.privatePort); await ensureDescriptorDirectory(this.paths); await this.recoverInterruptedPublications(); const descriptorPath = descriptorPathFor(this.paths, this.controllerPort); let existing: IControllerMcpDescriptorRecord | undefined; try { existing = await readSecureDescriptor(this.controllerPort, this.paths); } catch (errorArg) { if ( !(errorArg instanceof ControllerMcpDescriptorError) || errorArg.code !== 'CONTROLLER_UNAVAILABLE' ) throw errorArg; } if (existing) { if (await descriptorProcessIsLive(existing.descriptor, this.readProcessIdentity)) throw fenced(); this.pendingCleanup = this.createRemovalCleanupState(existing); await this.retryPendingCleanup(); } const descriptor: IControllerMcpDescriptor = { schemaVersion: controllerMcpDescriptorSchemaVersion, packageName: controllerPackageName, packageVersion: commitinfo.version, protocolVersion: controllerProtocolVersion, upgradeManagementVersion: controllerUpgradeManagementVersion, controllerPort: this.controllerPort, lifecycleGeneration: inputArg.lifecycleGeneration, origin: `http://127.0.0.1:${privatePort}`, privatePort, path: controllerMcpTypedRequestPath, controllerPid: inputArg.processIdentity.pid, processGroupId: inputArg.processIdentity.processGroupId, processFingerprint: inputArg.processIdentity.fingerprint, processStartTicks: inputArg.processIdentity.startTicks, token: plugins.crypto.randomBytes(32).toString('base64url'), publishedAt: Date.now(), }; const bytes = Buffer.from(serializeControllerMcpDescriptor(descriptor), 'utf8'); if (bytes.byteLength > controllerMcpDescriptorMaximumBytes) throw fenced(); const temporaryPath = plugins.path.join( this.paths.mcpRuntime, `.controller-${this.controllerPort}.${process.pid}-${plugins.crypto.randomBytes(16).toString('hex')}.tmp`, ); try { const handle = await plugins.fs.promises.open( temporaryPath, plugins.fs.constants.O_WRONLY | plugins.fs.constants.O_CREAT | plugins.fs.constants.O_EXCL | plugins.fs.constants.O_NOFOLLOW, 0o600, ); const cleanupState: IControllerMcpDescriptorCleanupState = { temporaryPath, destinationPath: descriptorPath, temporaryHandle: handle, retainedHandles: new Set([handle]), temporaryMayExist: true, destinationMayExist: false, directoryNeedsSync: true, }; this.pendingCleanup = cleanupState; await handle.writeFile(bytes); await handle.sync(); const temporaryStats = await handle.stat({ bigint: true }); assertSecureDescriptorStats(temporaryStats, { linkCount: 1n, exactSize: bytes.byteLength, }); cleanupState.temporaryIdentity = identityFromStats(temporaryStats); await closeFileHandle(handle); cleanupState.retainedHandles.delete(handle); cleanupState.temporaryHandle = undefined; cleanupState.destinationMayExist = true; let linkError: unknown; try { await plugins.fs.promises.link(temporaryPath, descriptorPath); } catch (errorArg) { linkError = errorArg; } const [linkedTemporaryStats, linkedDestinationStats] = await Promise.all([ lstatIfPresent(temporaryPath), lstatIfPresent(descriptorPath), ]); if ( !linkedTemporaryStats || !linkedDestinationStats || !identitiesEqual( identityFromStats(linkedTemporaryStats), cleanupState.temporaryIdentity, ) || !identitiesEqual( identityFromStats(linkedDestinationStats), cleanupState.temporaryIdentity, ) ) throw fenced(linkError); assertSecureDescriptorStats(linkedTemporaryStats, { linkCount: 2n, exactSize: bytes.byteLength, }); assertSecureDescriptorStats(linkedDestinationStats, { linkCount: 2n, exactSize: bytes.byteLength, }); try { await plugins.fs.promises.unlink(temporaryPath); cleanupState.temporaryMayExist = false; } catch (errorArg) { const currentTemporary = await lstatIfPresent(temporaryPath); if (currentTemporary) throw fenced(errorArg); cleanupState.temporaryMayExist = false; } const destinationStats = await plugins.fs.promises.lstat(descriptorPath, { bigint: true }); assertSecureDescriptorStats(destinationStats, { linkCount: 1n, exactSize: bytes.byteLength, }); if (!identitiesEqual( identityFromStats(destinationStats), cleanupState.temporaryIdentity, )) throw fenced(); await syncDirectory(this.paths.mcpRuntime, cleanupState); cleanupState.directoryNeedsSync = false; const published: IControllerMcpDescriptorRecord = { descriptor, fileIdentity: cleanupState.temporaryIdentity, }; this.published = published; if (this.pendingCleanup === cleanupState) this.pendingCleanup = undefined; return { ...descriptor }; } catch (errorArg) { const operationError = descriptorError(errorArg); let cleanupError: unknown; try { await this.retryPendingCleanup(); } catch (caughtErrorArg) { cleanupError = caughtErrorArg; } if (cleanupError !== undefined) { throw fenced(new AggregateError( [operationError, cleanupError], 'Descriptor publication and retained cleanup failed.', )); } throw operationError; } } private async recoverInterruptedPublications(): Promise { const descriptorPath = descriptorPathFor(this.paths, this.controllerPort); const temporaryNamePattern = new RegExp( `^\\.controller-${this.controllerPort}\\.([1-9][0-9]*)-[a-f0-9]{32}\\.tmp$`, ); const discovered: Array<{ name: string; creatorPid: number }> = []; let visitedEntries = 0; try { const directory = await plugins.fs.promises.opendir(this.paths.mcpRuntime); for await (const entry of directory) { visitedEntries += 1; if (visitedEntries > controllerMcpDescriptorMaximumDirectoryEntries) throw fenced(); const match = temporaryNamePattern.exec(entry.name); if (!match) continue; const creatorPid = Number(match[1]); if (!Number.isSafeInteger(creatorPid) || creatorPid < 1) throw fenced(); discovered.push({ name: entry.name, creatorPid }); } } catch (errorArg) { throw descriptorError(errorArg); } const validated: Array<{ path: string; identity: IControllerMcpDescriptorFileIdentity; linkCount: bigint; }> = []; for (const candidate of discovered) { const temporaryPath = plugins.path.join(this.paths.mcpRuntime, candidate.name); let temporaryStats: plugins.fs.BigIntStats | undefined; try { temporaryStats = await lstatIfPresent(temporaryPath); } catch (errorArg) { throw fenced(errorArg); } if (!temporaryStats) continue; if (temporaryStats.nlink !== 1n && temporaryStats.nlink !== 2n) throw fenced(); assertSecureDescriptorStats(temporaryStats, { linkCount: temporaryStats.nlink, allowEmpty: true, }); let creatorIdentity: IControllerProcessIdentity | null; try { creatorIdentity = await this.readProcessIdentity(candidate.creatorPid); } catch (errorArg) { throw fenced(errorArg); } if (creatorIdentity) throw fenced(); const temporaryIdentity = identityFromStats(temporaryStats); if (temporaryStats.nlink === 2n) { let destinationStats: plugins.fs.BigIntStats | undefined; try { destinationStats = await lstatIfPresent(descriptorPath); } catch (errorArg) { throw fenced(errorArg); } if ( !destinationStats || !identitiesEqual(identityFromStats(destinationStats), temporaryIdentity) ) throw fenced(); assertSecureDescriptorStats(destinationStats, { linkCount: 2n, allowEmpty: true, }); } validated.push({ path: temporaryPath, identity: temporaryIdentity, linkCount: temporaryStats.nlink, }); } for (const candidate of validated) { this.pendingCleanup = { temporaryPath: candidate.path, destinationPath: descriptorPath, temporaryIdentity: candidate.identity, retainedHandles: new Set(), temporaryMayExist: true, destinationMayExist: false, directoryNeedsSync: false, fenceTemporaryReplacement: true, expectedTemporaryLinkCount: candidate.linkCount, temporaryRequiresDestinationLink: candidate.linkCount === 2n, }; await this.retryPendingCleanup(); } } }