feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
+1
View File
@@ -0,0 +1 @@
dist/
+80
View File
@@ -0,0 +1,80 @@
# `@qinglong/cluster-control`
This private workspace package is the composition root for the QingLong 3.0
`cluster-control` profile artifact.
It owns the readiness-first database lifecycle and exposes both the proven
runtime pool and a public-contract PostgreSQL RunRepository to a caller-supplied
stack factory. It depends only on public `@qinglong/runtime-core` and
`@qinglong/cluster-postgres/runtime` exports, so the resident control plane does
not load executable migration DDL. It must never deep-import the legacy root
`back/**` tree.
Public subpath exports now separate responsibilities:
- `@qinglong/cluster-control/application` owns the probe listener, activation,
admission drain, runtime stack, Pool and listener shutdown order;
- `@qinglong/cluster-control/availability` provides the one-way, timer-free
`ready -> unavailable` fence used by the PostgreSQL Pool error path;
- `@qinglong/cluster-control/http` provides bounded `/livez`, `/readyz` and
fail-closed `/api/v3` admission transport;
- `@qinglong/cluster-control/config` parses the Profile gate before reading the
runtime database credential/API credential pepper and defaults PostgreSQL to
verified TLS;
- `@qinglong/cluster-control/api-credential` validates the versioned `ql3c`
bearer format and authenticates a stable subject with a constant-time,
peppered digest comparison;
- `@qinglong/cluster-control/admission` resolves a route, authenticates a stable
principal, evaluates Policy and records the security decision before the HTTP
adapter is allowed to read the bounded request body;
- `@qinglong/cluster-control/routes` compiles an immutable, bounded and
non-overlapping route table whose operation, permission, Project path
parameter and query allowlist are fixed at startup.
- `@qinglong/cluster-control/run-routes` defines the first reviewed business
route: a Project-scoped `run.get` point query that returns only an explicit
low-sensitive DTO and masks cross-Project existence.
- `@qinglong/cluster-control/production` is the only reviewed production route
composition, fixes the current allowlist to `run.get` and `run.cancel`, and
atomically derives the runtime Pool plus its availability fence from one
enabled configuration so deployments cannot miswire those authorities;
- `@qinglong/cluster-control/s3-artifact-store` is a separately lazy-loaded,
cluster-only immutable Artifact adapter; neither the package root nor the
production API entrypoint loads the AWS SDK;
- `@qinglong/cluster-control/worker-ingress` owns the separately gated TLS 1.3
mutual-TLS listener and exposes explicit secure-context reload without
acquiring database adapter or CA-signing authority;
- `@qinglong/cluster-control/worker-ingress-config` loads bounded server
identity, 116 client CA certificates and an optional CRL after the Profile
gate, builds only the dedicated Worker HTTP/Pool options, and validates the
immutable S3 Artifact binding;
- `@qinglong/cluster-control/worker-runtime-port` constructs the frozen
in-process offer/ACK/Artifact/completion/lease capability boundary without
exposing the runtime Pool;
- `@qinglong/cluster-control/worker-ingress-production` combines that port with
the independent Worker credential/Session/attestation/audit Pool.
The cluster assembly now supplies real PostgreSQL API Credential, Project
Policy and write-only Security Audit repositories, plus a bounded recovery
candidate source. After the caller's recovery reports safe convergence, the
bootstrap independently verifies PostgreSQL has no orphaned or expired-lease
Run/Attempt candidate; a false-safe summary cannot open admission. Admission accepts only a
route resolver produced by the reviewed registry factory; a caller cannot
silently replace it with an ad-hoc resolver. The tested vertical path is HTTP
bearer authentication → fenced Project Policy → durable low-sensitive audit →
bounded body/handler.
The production application registers the reviewed `run.get` and `run.cancel`
routes through one static allowlist and, when explicitly enabled, starts the
independent 5801 mTLS Worker listener after readiness/recovery. Runtime
Run/Attempt/Lease mutation stays behind the injected capability port;
`ql3_worker_ingress` receives no such database grant. Artifact S3 support is
loaded only on that enabled path, so disabled Cluster and all local Profiles do
not load its provider.
Identity/credential administration remains in the separate short-lived
cluster-admin authority, and the resident HTTP surfaces retain bounded
authentication overload shields. Remaining incubation gaps include Cluster
Secret material provider/rotation, Remote Worker expiry/retry production
lifecycle, audit retention/export/alerting and real multi-Pod
operator/proxy/STONITH capacity evidence. The generic admission pipeline must
not be wired to an allow-all authenticator or Policy in production.
+187
View File
@@ -0,0 +1,187 @@
{
"name": "@qinglong/cluster-control",
"version": "3.0.0-alpha.0",
"private": true,
"description": "QingLong 3.0 cluster-control composition root",
"license": "Apache-2.0",
"engines": {
"node": ">=24.18.0 <25"
},
"main": "dist/application-runtime/clusterControlRuntime.js",
"types": "dist/application-runtime/clusterControlRuntime.d.ts",
"exports": {
".": {
"types": "./dist/application-runtime/clusterControlRuntime.d.ts",
"require": "./dist/application-runtime/clusterControlRuntime.js",
"default": "./dist/application-runtime/clusterControlRuntime.js"
},
"./application": {
"types": "./dist/application-runtime/application.d.ts",
"require": "./dist/application-runtime/application.js",
"default": "./dist/application-runtime/application.js"
},
"./availability": {
"types": "./dist/database/availability.d.ts",
"require": "./dist/database/availability.js",
"default": "./dist/database/availability.js"
},
"./production": {
"types": "./dist/application-runtime/productionApplication.d.ts",
"require": "./dist/application-runtime/productionApplication.js",
"default": "./dist/application-runtime/productionApplication.js"
},
"./ai-production": {
"types": "./dist/application-runtime/aiProductionApplication.d.ts",
"require": "./dist/application-runtime/aiProductionApplication.js",
"default": "./dist/application-runtime/aiProductionApplication.js"
},
"./http": {
"types": "./dist/transport/httpSurface.d.ts",
"require": "./dist/transport/httpSurface.js",
"default": "./dist/transport/httpSurface.js"
},
"./config": {
"types": "./dist/production-process/config.d.ts",
"require": "./dist/production-process/config.js",
"default": "./dist/production-process/config.js"
},
"./process": {
"types": "./dist/production-process/processApplication.d.ts",
"require": "./dist/production-process/processApplication.js",
"default": "./dist/production-process/processApplication.js"
},
"./admission": {
"types": "./dist/transport/admissionPipeline.d.ts",
"require": "./dist/transport/admissionPipeline.js",
"default": "./dist/transport/admissionPipeline.js"
},
"./routes": {
"types": "./dist/transport/routeRegistry.d.ts",
"require": "./dist/transport/routeRegistry.js",
"default": "./dist/transport/routeRegistry.js"
},
"./run-routes": {
"types": "./dist/run/runReadRoute.d.ts",
"require": "./dist/run/runReadRoute.js",
"default": "./dist/run/runReadRoute.js"
},
"./task-routes": {
"types": "./dist/task/taskListRoute.d.ts",
"require": "./dist/task/taskListRoute.js",
"default": "./dist/task/taskListRoute.js"
},
"./prompt-routes": {
"types": "./dist/plugin-package/prompt/pluginPackagePromptRoutes.d.ts",
"require": "./dist/plugin-package/prompt/pluginPackagePromptRoutes.js",
"default": "./dist/plugin-package/prompt/pluginPackagePromptRoutes.js"
},
"./api-credential": {
"types": "./dist/authentication/apiCredentialAuthenticator.d.ts",
"require": "./dist/authentication/apiCredentialAuthenticator.js",
"default": "./dist/authentication/apiCredentialAuthenticator.js"
},
"./worker-ingress": {
"types": "./dist/worker-ingress/workerIngressApplication.d.ts",
"require": "./dist/worker-ingress/workerIngressApplication.js",
"default": "./dist/worker-ingress/workerIngressApplication.js"
},
"./worker-ingress-config": {
"types": "./dist/worker-ingress/workerIngressConfig.d.ts",
"require": "./dist/worker-ingress/workerIngressConfig.js",
"default": "./dist/worker-ingress/workerIngressConfig.js"
},
"./worker-ingress-production": {
"types": "./dist/worker-ingress/productionWorkerIngress.d.ts",
"require": "./dist/worker-ingress/productionWorkerIngress.js",
"default": "./dist/worker-ingress/productionWorkerIngress.js"
},
"./worker-runtime-port": {
"types": "./dist/remote-execution/workerRuntimePort.d.ts",
"require": "./dist/remote-execution/workerRuntimePort.js",
"default": "./dist/remote-execution/workerRuntimePort.js"
},
"./remote-dispatch": {
"types": "./dist/remote-execution/remoteWorkerDispatcher.d.ts",
"require": "./dist/remote-execution/remoteWorkerDispatcher.js",
"default": "./dist/remote-execution/remoteWorkerDispatcher.js"
},
"./remote-activation": {
"types": "./dist/remote-execution/remoteRunActivationService.d.ts",
"require": "./dist/remote-execution/remoteRunActivationService.js",
"default": "./dist/remote-execution/remoteRunActivationService.js"
},
"./remote-secret-delivery": {
"types": "./dist/remote-execution/remoteWorkerSecretDeliveryService.d.ts",
"require": "./dist/remote-execution/remoteWorkerSecretDeliveryService.js",
"default": "./dist/remote-execution/remoteWorkerSecretDeliveryService.js"
},
"./mounted-secret-provider": {
"types": "./dist/remote-execution/mountedSecretProvider.d.ts",
"require": "./dist/remote-execution/mountedSecretProvider.js",
"default": "./dist/remote-execution/mountedSecretProvider.js"
},
"./remote-completion": {
"types": "./dist/remote-execution/remoteWorkerCompletionService.d.ts",
"require": "./dist/remote-execution/remoteWorkerCompletionService.js",
"default": "./dist/remote-execution/remoteWorkerCompletionService.js"
},
"./s3-artifact-store": {
"types": "./dist/artifact/s3ArtifactStore.d.ts",
"require": "./dist/artifact/s3ArtifactStore.js",
"default": "./dist/artifact/s3ArtifactStore.js"
},
"./lease-control": {
"types": "./dist/remote-execution/remoteWorkerLeaseControlService.d.ts",
"require": "./dist/remote-execution/remoteWorkerLeaseControlService.js",
"default": "./dist/remote-execution/remoteWorkerLeaseControlService.js"
},
"./workflow-scheduler": {
"types": "./dist/scheduling/workflowScheduler.d.ts",
"require": "./dist/scheduling/workflowScheduler.js",
"default": "./dist/scheduling/workflowScheduler.js"
},
"./workflow-administration": {
"types": "./dist/plugin-package/workflow/pluginPackageWorkflowAdministration.d.ts",
"require": "./dist/plugin-package/workflow/pluginPackageWorkflowAdministration.js",
"default": "./dist/plugin-package/workflow/pluginPackageWorkflowAdministration.js"
},
"./workflow-routes": {
"types": "./dist/plugin-package/workflow/pluginPackageWorkflowRoute.d.ts",
"require": "./dist/plugin-package/workflow/pluginPackageWorkflowRoute.js",
"default": "./dist/plugin-package/workflow/pluginPackageWorkflowRoute.js"
}
},
"files": [
"dist/**/*.js",
"dist/**/*.d.ts"
],
"bin": {
"ql3-cluster-control": "dist/cli.js",
"ql3-cluster-control-ai": "dist/aiCli.js"
},
"scripts": {
"build": "tsc -p tsconfig.json",
"check": "node ../../scripts/ql3-build-package-closure.cjs && tsc -p tsconfig.json --noEmit",
"test": "node ../../scripts/ql3-build-package-closure.cjs && node --test test/*.test.cjs",
"test:integration": "node ../../scripts/ql3-build-package-closure.cjs && node --test test/postgres.integration.test.cjs"
},
"dependencies": {
"@aws-sdk/client-s3": "3.1093.0",
"@qinglong/cluster-postgres": "workspace:*",
"@qinglong/runtime-core": "workspace:*",
"croner": "7.0.8"
},
"devDependencies": {
"@qinglong/ai": "workspace:*",
"@types/node": "24.13.3",
"typescript": "5.9.3"
},
"peerDependencies": {
"@qinglong/ai": "workspace:*"
},
"peerDependenciesMeta": {
"@qinglong/ai": {
"optional": true
}
}
}
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env node
import type { ModelGatewayProfileAudit } from '@qinglong/ai/profile';
import {
loadProductionClusterAiConfig,
startProductionClusterAiControlApplication,
} from './application-runtime/aiProductionApplication';
import {
runProductionClusterControlProcess,
type ClusterControlProcessEvent,
type ClusterControlProcessSignal,
type ClusterControlProcessSignalSource,
} from './production-process/processApplication';
const USAGE = 'Usage: ql3-cluster-control-ai';
const nodeSignals: ClusterControlProcessSignalSource = Object.freeze({
subscribe(listener: (signal: ClusterControlProcessSignal) => void) {
const handlers = Object.freeze({
SIGINT: () => listener('SIGINT' as const),
SIGTERM: () => listener('SIGTERM' as const),
});
process.once('SIGINT', handlers.SIGINT);
process.once('SIGTERM', handlers.SIGTERM);
return () => {
process.off('SIGINT', handlers.SIGINT);
process.off('SIGTERM', handlers.SIGTERM);
};
},
});
function write(record: object): void {
process.stdout.write(`${JSON.stringify(record)}\n`);
}
function emit(record: ClusterControlProcessEvent): void {
write(record);
}
function audit(record: Readonly<ModelGatewayProfileAudit>): void {
write(
Object.freeze({
schemaVersion: 1,
component: 'qinglong3-cluster-ai',
level: record.state === 'failed' ? 'error' : 'info',
event: 'activation',
profile: record.profile,
state: record.state,
...(record.maxConcurrent === undefined
? {}
: { maxConcurrent: record.maxConcurrent }),
...(record.recoveryLimit === undefined
? {}
: { recoveryLimit: record.recoveryLimit }),
...(record.recovered === undefined
? {}
: { recovered: record.recovered }),
...(record.alreadyCompleted === undefined
? {}
: { alreadyCompleted: record.alreadyCompleted }),
}),
);
}
async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`);
return;
}
if (argv.length !== 0) {
process.stderr.write(
`${JSON.stringify({
code: 'QL3_CLUSTER_AI_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const ai = loadProductionClusterAiConfig(process.env);
const stopResult = await runProductionClusterControlProcess({
environment: process.env,
signals: nodeSignals,
emit,
start: (control) =>
startProductionClusterAiControlApplication({ control, ai, audit }),
});
if (stopResult !== 'stopped') process.exitCode = 1;
} catch (error) {
const candidate = error as { readonly name?: unknown; readonly code?: unknown };
process.stderr.write(
`${JSON.stringify({
schemaVersion: 1,
component: 'qinglong3-cluster-ai',
level: 'error',
event: 'process_failed',
name:
typeof candidate?.name === 'string' ? candidate.name : 'Error',
...(typeof candidate?.code === 'string'
? { code: candidate.code }
: {}),
})}\n`,
);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,365 @@
import type { ModelGatewayProfileAudit } from '@qinglong/ai/profile';
import { BoundModelProviderCredentialProvider } from '@qinglong/ai/provider-credential';
import { PostgresModelProviderCredentialReader } from '@qinglong/ai/postgres-model-provider-credential-storage';
import { loadProjectedModelGatewayProviderAuthority } from '@qinglong/ai/projected-model-gateway-authority';
import { createProjectedModelProviderSecretMaterialProvider } from '@qinglong/ai/projected-model-provider-secret-material';
import { createPluginPackagePromptOutputProjectedKeyring } from '@qinglong/ai/plugin-package-prompt-output-projected-keyring';
import {
bootstrapPostgresPluginPackagePromptApplication,
type BootstrapPostgresPluginPackagePromptApplicationResult,
} from '@qinglong/ai/postgres-plugin-package-prompt-application';
import { createPostgresDatabaseOpener } from '@qinglong/cluster-postgres/runtime';
import { PostgresProjectPolicyRepository } from '@qinglong/cluster-postgres/project-policy';
import type { ClusterControlStopResult } from '@qinglong/runtime-core';
import { ProjectPolicyEngine } from '@qinglong/runtime-core/project-policy';
import type { ClusterControlApplicationResult } from './application';
import type {
ClusterControlEnvironment,
EnabledClusterControlConfig,
} from '../production-process/config';
import {
startProductionClusterControlApplication,
type ProductionClusterControlApplicationOptions,
} from './productionApplication';
export interface EnabledProductionClusterAiConfig {
readonly enabled: true;
readonly providerAuthorityFile: string;
readonly secretRootDirectory: string;
readonly promptOutputKeyringRootDirectory?: string;
readonly maxConcurrent: number;
readonly recoveryLimit: number;
readonly databaseMaxConnections: number;
}
export interface ProductionClusterAiControlApplicationOptions {
readonly control: ProductionClusterControlApplicationOptions;
readonly ai: EnabledProductionClusterAiConfig;
readonly audit: (
record: Readonly<ModelGatewayProfileAudit>,
) => void | Promise<void>;
readonly startControl?: typeof startProductionClusterControlApplication;
readonly bootstrapPrompt?: typeof bootstrapPostgresPluginPackagePromptApplication;
}
export class ProductionClusterAiConfigError extends TypeError {
readonly code = 'QL3_CLUSTER_AI_CONFIG_INVALID';
constructor(message: string) {
super(`Cluster AI configuration is invalid: ${message}`);
this.name = 'ProductionClusterAiConfigError';
}
}
function booleanValue(
environment: ClusterControlEnvironment,
name: string,
defaultValue: boolean,
): boolean {
const value = environment[name];
if (value === undefined || value === '') return defaultValue;
if (value === 'true') return true;
if (value === 'false') return false;
throw new ProductionClusterAiConfigError(`${name} must be true or false`);
}
function boundedInteger(
environment: ClusterControlEnvironment,
name: string,
fallback: number,
minimum: number,
maximum: number,
): number {
const value = environment[name];
if (value === undefined || value === '') return fallback;
if (!/^\d+$/.test(value)) {
throw new ProductionClusterAiConfigError(`${name} must be an integer`);
}
const parsed = Number(value);
if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) {
throw new ProductionClusterAiConfigError(
`${name} must be between ${minimum} and ${maximum}`,
);
}
return parsed;
}
function requiredPath(
environment: ClusterControlEnvironment,
name: string,
): string {
const value = environment[name];
if (
typeof value !== 'string' ||
value.length < 2 ||
value.length > 4096 ||
!value.startsWith('/') ||
/[\0\r\n]/.test(value)
) {
throw new ProductionClusterAiConfigError(`${name} is invalid`);
}
return value;
}
/** Parsed only by the explicit AI process entrypoint; the default CLI ignores it. */
export function loadProductionClusterAiConfig(
environment: ClusterControlEnvironment,
): EnabledProductionClusterAiConfig {
if (
!environment ||
typeof environment !== 'object' ||
Array.isArray(environment) ||
!booleanValue(environment, 'QL3_CLUSTER_AI_ENABLED', false)
) {
throw new ProductionClusterAiConfigError(
'QL3_CLUSTER_AI_ENABLED must be true',
);
}
const promptOutputEnabled = booleanValue(
environment,
'QL3_CLUSTER_AI_PROMPT_OUTPUT_ENABLED',
false,
);
return Object.freeze({
enabled: true,
providerAuthorityFile: requiredPath(
environment,
'QL3_CLUSTER_AI_PROVIDER_AUTHORITY_FILE',
),
secretRootDirectory: requiredPath(
environment,
'QL3_CLUSTER_AI_SECRET_ROOT',
),
...(promptOutputEnabled
? {
promptOutputKeyringRootDirectory: requiredPath(
environment,
'QL3_CLUSTER_AI_PROMPT_OUTPUT_KEYRING_ROOT',
),
}
: {}),
maxConcurrent: boundedInteger(
environment,
'QL3_CLUSTER_AI_MAX_CONCURRENT',
4,
1,
64,
),
recoveryLimit: boundedInteger(
environment,
'QL3_CLUSTER_AI_RECOVERY_LIMIT',
32,
1,
128,
),
databaseMaxConnections: boundedInteger(
environment,
'QL3_CLUSTER_AI_DATABASE_MAX_CONNECTIONS',
4,
1,
16,
),
});
}
function aiDatabaseOpener(
control: EnabledClusterControlConfig,
ai: EnabledProductionClusterAiConfig,
onUnavailable: (error: Error) => void,
) {
return createPostgresDatabaseOpener({
role: 'runtime',
connection: control.database.connection,
pool: {
...control.database.pool,
applicationName: 'qinglong-cluster-ai',
maxConnections: ai.databaseMaxConnections,
},
onPoolError: onUnavailable,
});
}
/**
* Explicit AI-enabled composition root. It keeps the normal control image and
* process AI-free while sharing the reviewed authentication/Policy pipeline
* and route registry when the separate AI image entrypoint is selected.
*/
export async function startProductionClusterAiControlApplication(
options: ProductionClusterAiControlApplicationOptions,
): Promise<ClusterControlApplicationResult> {
if (
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
typeof options.audit !== 'function'
) {
throw new TypeError('Production Cluster AI application options are invalid');
}
const startControl =
options.startControl ?? startProductionClusterControlApplication;
const bootstrapPrompt =
options.bootstrapPrompt ?? bootstrapPostgresPluginPackagePromptApplication;
if (typeof startControl !== 'function' || typeof bootstrapPrompt !== 'function') {
throw new TypeError('Production Cluster AI application factories are invalid');
}
const secretMaterial =
await createProjectedModelProviderSecretMaterialProvider({
rootDirectory: options.ai.secretRootDirectory,
});
const promptOutputKeys =
options.ai.promptOutputKeyringRootDirectory === undefined
? undefined
: await createPluginPackagePromptOutputProjectedKeyring({
rootDirectory: options.ai.promptOutputKeyringRootDirectory,
});
let aiDatabase:
| Awaited<ReturnType<ReturnType<typeof createPostgresDatabaseOpener>>>
| undefined;
let resolveAiUnavailable: ((error: Error) => void) | undefined;
let aiUnavailableError: Error | undefined;
const aiUnavailable = new Promise<Error>((resolve) => {
resolveAiUnavailable = resolve;
});
const onAiUnavailable = (error: Error): void => {
aiUnavailableError ??= error;
resolveAiUnavailable?.(aiUnavailableError);
resolveAiUnavailable = undefined;
};
let promptApplication:
| BootstrapPostgresPluginPackagePromptApplicationResult
| undefined;
let controlApplication: ClusterControlApplicationResult | undefined;
let stopPromise: Promise<ClusterControlStopResult> | undefined;
let promptOutputPolicy: ProjectPolicyEngine | undefined;
const promptOutputReadAuthorizer = Object.freeze({
async authorize(request: Readonly<{
principal: Parameters<ProjectPolicyEngine['authorize']>[0];
projectId: string;
}>) {
if (!aiDatabase) {
throw new Error('Cluster AI database is unavailable during output read');
}
promptOutputPolicy ??= new ProjectPolicyEngine(
new PostgresProjectPolicyRepository(aiDatabase.pool),
);
const decision = await promptOutputPolicy.authorize(
request.principal,
request.projectId,
'artifact.read',
);
return decision.effect === 'allow'
? Object.freeze({ effect: 'allow' as const })
: Object.freeze({
effect: decision.effect,
reasonCode: 'artifact_read_denied',
});
},
});
const stop = async (): Promise<ClusterControlStopResult> => {
stopPromise ??= (async () => {
const controlResult =
controlApplication?.status === 'active'
? await controlApplication.stop()
: 'stopped';
const promptResult = await promptApplication?.stop();
return controlResult === 'stopped' &&
(promptResult === undefined || promptResult === 'stopped')
? 'stopped'
: 'timed_out';
})();
return stopPromise;
};
try {
const openDatabase = aiDatabaseOpener(
options.control.config,
options.ai,
onAiUnavailable,
);
promptApplication = await bootstrapPrompt({
enabled: true,
async openDatabase() {
if (aiDatabase) {
throw new Error('Cluster AI database was opened more than once');
}
aiDatabase = await openDatabase();
return aiDatabase;
},
async loadProviders() {
if (!aiDatabase) {
throw new Error('Cluster AI database is unavailable during provider load');
}
const credentialStorage = new PostgresModelProviderCredentialReader(
aiDatabase.pool,
);
const credentials = new BoundModelProviderCredentialProvider({
bindings: credentialStorage,
secrets: secretMaterial,
audit: credentialStorage,
});
return loadProjectedModelGatewayProviderAuthority({
configFile: options.ai.providerAuthorityFile,
credentials,
});
},
audit: options.audit,
maxConcurrent: options.ai.maxConcurrent,
recoveryLimit: options.ai.recoveryLimit,
...(promptOutputKeys === undefined
? {}
: {
promptOutputKeys,
promptOutputRead: { authorizer: promptOutputReadAuthorizer },
}),
});
if (promptApplication.status !== 'active') {
throw new Error('Cluster AI Prompt application did not activate');
}
controlApplication = await startControl({
...options.control,
promptCatalog: {
capability: promptApplication.promptCatalog,
},
promptExecution: {
capability: promptApplication.promptExecutions,
},
promptExecutionInspection: {
capability: promptApplication.promptExecutionInspections,
},
...(promptApplication.promptOutputs === undefined
? {}
: {
promptOutputRead: {
capability: promptApplication.promptOutputs,
},
}),
...(promptApplication.promptExecutionOutputs === undefined
? {}
: {
promptExecutionOutputRead: {
capability: promptApplication.promptExecutionOutputs,
},
}),
});
if (controlApplication.status !== 'active') {
throw new Error('AI-enabled cluster-control did not activate');
}
const activeControl = controlApplication;
return Object.freeze({
status: 'active' as const,
address: activeControl.address,
evidence: activeControl.evidence,
recovery: activeControl.recovery,
unavailable: Promise.race([activeControl.unavailable, aiUnavailable]),
availabilityStatus() {
return aiUnavailableError
? 'unavailable'
: activeControl.availabilityStatus();
},
stop,
});
} catch (error) {
await stop().catch(() => undefined);
throw error;
}
}
@@ -0,0 +1,244 @@
import type {
ClusterControlActivationAudit,
ClusterControlActivationStack,
ClusterControlReadinessEvidence,
ClusterControlRuntimeActivationResult,
ClusterControlStartupRecoverySummary,
ClusterControlStopResult,
DeploymentProfile,
OpenPostgresDatabase,
} from '@qinglong/runtime-core';
import {
bootstrapClusterControlRuntime,
type ClusterControlAssemblyInput,
type ClusterControlRecoveryRuntimeOptions,
type ClusterRunCancellationConvergenceRuntimeOptions,
type ClusterSchedulerRuntimeOptions,
type ClusterWorkerRuntimeDependencies,
} from './clusterControlRuntime';
import { assertClusterControlApiCredentialPepper } from '../authentication/apiCredentialAuthenticator';
import {
startClusterControlHttpSurface,
type ClusterControlAdmissionPipeline,
type ClusterControlHttpAddress,
type ClusterControlHttpSurfaceOptions,
} from '../transport/httpSurface';
import type { ClusterControlAvailabilitySource } from '../database/availability';
export interface ClusterControlApplicationStack {
reconcile(): Promise<ClusterControlStartupRecoverySummary>;
startLifecycles(): Promise<boolean>;
admission: ClusterControlAdmissionPipeline;
stop(): Promise<ClusterControlStopResult>;
}
export interface ClusterControlApplicationOptions {
readonly enabled?: boolean;
readonly profile: DeploymentProfile;
readonly apiCredentialPepper?: string;
readonly recovery?: ClusterControlRecoveryRuntimeOptions;
readonly scheduler?: ClusterSchedulerRuntimeOptions;
readonly cancellationConvergence?: ClusterRunCancellationConvergenceRuntimeOptions;
readonly workerRuntime?: ClusterWorkerRuntimeDependencies;
readonly openDatabase: OpenPostgresDatabase;
readonly availability: ClusterControlAvailabilitySource;
readonly http: ClusterControlHttpSurfaceOptions;
readonly create: (
input: ClusterControlAssemblyInput,
) => ClusterControlApplicationStack;
readonly audit: (
record: ClusterControlActivationAudit,
) => void | Promise<void>;
}
export type ClusterControlApplicationResult =
| { readonly status: 'disabled'; stop(): Promise<'stopped'> }
| {
readonly status: 'active';
readonly address: ClusterControlHttpAddress;
readonly evidence: ClusterControlReadinessEvidence;
readonly recovery: ClusterControlStartupRecoverySummary;
readonly unavailable: Promise<Error>;
availabilityStatus(): 'ready' | 'unavailable' | 'stopped';
stop(): Promise<ClusterControlStopResult>;
};
export class ClusterControlDatabaseUnavailableError extends Error {
readonly code = 'CLUSTER_CONTROL_DATABASE_UNAVAILABLE';
constructor() {
super('Cluster-control database became unavailable');
this.name = 'ClusterControlDatabaseUnavailableError';
}
}
function inactiveBootstrap(
options: ClusterControlApplicationOptions,
): Promise<ClusterControlRuntimeActivationResult> {
return bootstrapClusterControlRuntime({
...(options.enabled === undefined ? {} : { enabled: options.enabled }),
profile: options.profile,
...(options.apiCredentialPepper === undefined
? {}
: { apiCredentialPepper: options.apiCredentialPepper }),
...(options.recovery === undefined ? {} : { recovery: options.recovery }),
...(options.scheduler === undefined
? {}
: { scheduler: options.scheduler }),
...(options.cancellationConvergence === undefined
? {}
: { cancellationConvergence: options.cancellationConvergence }),
...(options.workerRuntime === undefined
? {}
: { workerRuntime: options.workerRuntime }),
openDatabase: options.openDatabase,
create() {
throw new Error('Inactive cluster-control unexpectedly created a stack');
},
audit: options.audit,
});
}
/**
* Starts the cluster probe surface before database readiness, then installs the
* /api/v3 admission handler only after recovery and lifecycles are safe. Stop
* withdraws and drains admission before stack, Pool and listener shutdown.
*/
export async function startClusterControlApplication(
options: ClusterControlApplicationOptions,
): Promise<ClusterControlApplicationResult> {
const enabled = options.enabled ?? false;
if (!enabled || options.profile !== 'cluster-control') {
const inactive = await inactiveBootstrap(options);
if (inactive.status !== 'disabled') {
throw new Error('Inactive cluster-control unexpectedly became active');
}
return inactive;
}
assertClusterControlApiCredentialPepper(options.apiCredentialPepper ?? '');
const apiCredentialPepper = options.apiCredentialPepper!;
if (
!options.availability ||
typeof options.availability.subscribe !== 'function'
) {
throw new TypeError('Cluster-control availability source is invalid');
}
const http = await startClusterControlHttpSurface(options.http);
let activation: ClusterControlRuntimeActivationResult | undefined;
let unavailableError: Error | undefined;
let unavailableStopPromise: Promise<ClusterControlStopResult> | undefined;
let availabilityStatus: 'ready' | 'unavailable' | 'stopped' = 'ready';
let unsubscribeAvailability: (() => void) | undefined;
let resolveUnavailable: ((error: Error) => void) | undefined;
const unavailable = new Promise<Error>((resolve) => {
resolveUnavailable = resolve;
});
const withdrawForUnavailable = (error: Error): Promise<void> => {
unavailableError ??= error;
availabilityStatus = 'unavailable';
resolveUnavailable?.(unavailableError);
resolveUnavailable = undefined;
if (!activation || activation.status === 'disabled')
return Promise.resolve();
unavailableStopPromise ??= activation.stop();
return unavailableStopPromise.then(
() => undefined,
() => undefined,
);
};
try {
unsubscribeAvailability = options.availability.subscribe(
withdrawForUnavailable,
);
activation = await bootstrapClusterControlRuntime({
enabled: true,
profile: options.profile,
apiCredentialPepper,
...(options.recovery === undefined ? {} : { recovery: options.recovery }),
...(options.scheduler === undefined
? {}
: { scheduler: options.scheduler }),
...(options.cancellationConvergence === undefined
? {}
: { cancellationConvergence: options.cancellationConvergence }),
...(options.workerRuntime === undefined
? {}
: { workerRuntime: options.workerRuntime }),
openDatabase: options.openDatabase,
create(input): ClusterControlActivationStack {
const application = options.create(input);
if (
!application ||
typeof application !== 'object' ||
!application.admission ||
typeof application.admission.prepare !== 'function'
) {
throw new TypeError(
'Cluster-control application stack has no admission pipeline',
);
}
return {
reconcile: () => application.reconcile(),
startLifecycles: () => application.startLifecycles(),
installAdmission: () =>
http.installAdmission(input.evidence, application.admission),
stop: () => application.stop(),
};
},
audit: options.audit,
});
if (activation.status === 'disabled') {
unsubscribeAvailability();
await http.close();
return activation;
}
if (unavailableError) {
await withdrawForUnavailable(unavailableError);
throw new ClusterControlDatabaseUnavailableError();
}
const activeActivation = activation;
let stopPromise: Promise<ClusterControlStopResult> | undefined;
return {
status: 'active',
address: http.address,
evidence: activeActivation.evidence,
recovery: activeActivation.recovery,
unavailable,
availabilityStatus: () => availabilityStatus,
stop() {
if (stopPromise) return stopPromise;
availabilityStatus = 'stopped';
unsubscribeAvailability?.();
unsubscribeAvailability = undefined;
stopPromise = (async () => {
let result: ClusterControlStopResult | undefined;
let primaryError: unknown;
try {
result = await activeActivation.stop();
} catch (error) {
primaryError = error;
}
try {
await http.close();
} catch (error) {
primaryError ??= error;
}
if (primaryError) throw primaryError;
return result!;
})();
return stopPromise;
},
};
} catch (error) {
unsubscribeAvailability?.();
try {
await http.close();
} catch {
// Preserve the readiness/assembly/activation failure.
}
throw error;
}
}
@@ -0,0 +1,842 @@
import { randomUUID } from 'node:crypto';
import {
activateClusterControlRuntime,
ClusterRunLostRetryCoordinator,
ClusterRunCancellationConvergenceCoordinator,
ClusterControlRecoveryEvidenceRegistry,
ClusterControlRecoveryConvergenceVerifier,
ClusterControlRecoverySupervisor,
ClusterControlStartupRecoveryCoordinator,
EvidenceBasedClusterControlRecoveryProcessor,
MAX_CLUSTER_CONTROL_RECOVERY_CLAIMS_PER_PASS,
MAX_CLUSTER_CONTROL_RECOVERY_CLAIM_LEASE_MS,
MAX_CLUSTER_CONTROL_RECOVERY_EVIDENCE_TIMEOUT_MS,
MAX_CLUSTER_CONTROL_RECOVERY_RETRY_DELAY_MS,
MAX_CLUSTER_CONTROL_STARTUP_RECOVERY_PASSES,
MAX_CLUSTER_RUN_CANCELLATION_CONVERGENCE_PAGE_SIZE,
MAX_CLUSTER_RUN_CANCELLATION_CONVERGENCE_PAGES_PER_CYCLE,
type ClusterControlActivationAudit,
type ClusterControlActivationStack,
type ClusterControlReadinessEvidence,
type ClusterControlRecoveryExecutorEvidenceProvider,
type ClusterControlRuntimeActivationResult,
type ClusterControlStopResult,
type DeploymentProfile,
type OpenPostgresDatabase,
type PostgresDatabaseResource,
type PostgresPool,
type ProjectPolicyRepository,
type RunRepository,
type ClusterRunCancellationConvergenceCycleResult,
} from '@qinglong/runtime-core';
import type { ClusterRunCancellationRepository } from '@qinglong/runtime-core/cluster-run-cancellation';
import type { ProjectRunListReader } from '@qinglong/runtime-core/project-run-list';
import type { ClusterScheduleStore } from '@qinglong/runtime-core/cluster-scheduler';
import type { TaskDefinitionSource } from '@qinglong/runtime-core/task-definition';
import type { TriggerSource } from '@qinglong/runtime-core/trigger';
import type { ClusterTaskExecutionRevisionSource } from '@qinglong/runtime-core/cluster-execution-revision';
import type {
ProjectToolDefinitionSnapshotRepository,
ProjectToolDefinitionSnapshotSourceRepository,
} from '@qinglong/runtime-core/project-tool-definition-snapshot';
import type { StepRunRepository } from '@qinglong/runtime-core/step-run';
import type { ToolExecutionCompletionRepository } from '@qinglong/runtime-core/tool-execution-completion';
import type { ToolExecutionFailureCompletionRepository } from '@qinglong/runtime-core/tool-execution-failure-completion';
import type { ToolExecutionStartBarrierRepository } from '@qinglong/runtime-core/tool-execution-start-barrier';
import type { ToolInvocationArtifactRepository } from '@qinglong/runtime-core/tool-invocation-artifact';
import type { ToolResultKeyCatalogReader } from '@qinglong/runtime-core/tool-result-key-catalog';
import type { ToolExecutionResultRekeyReader } from '@qinglong/runtime-core/tool-result-rekey';
import {
assertPostgresSchemaReady,
PostgresClusterControlRecoverySource,
PostgresClusterControlRecoveryClaimRepository,
PostgresClusterControlRecoveryResolutionRepository,
PostgresClusterRuntimeRecoverySource,
PostgresClusterRunLostRetryRepository,
PostgresClusterRunCancellationRepository,
PostgresClusterRunCancellationConvergenceRepository,
PostgresProjectPolicyRepository,
PostgresApiCredentialRepository,
PostgresSecurityAuditRepository,
PostgresRunRepository,
PostgresWorkerExecutionAttestationRepository,
PostgresTaskDefinitionSource,
PostgresTaskExecutionRevisionSource,
PostgresTriggerSource,
PostgresClusterScheduleRepository,
PostgresRemoteWorkerAttestationEvidenceProvider,
PostgresProjectToolDefinitionSnapshotRepository,
PostgresStepRunRepository,
PostgresToolExecutionCompletionRepository,
PostgresToolExecutionFailureCompletionRepository,
PostgresToolExecutionStartBarrierRepository,
PostgresToolInvocationArtifactRepository,
PostgresToolResultKeyCatalogReader,
PostgresToolResultRekeyReader,
type PostgresSchemaReadinessReport,
} from '@qinglong/cluster-postgres/runtime';
import { PostgresPluginPackageWorkflowFrontierRepository } from '@qinglong/cluster-postgres/plugin-package-workflow-frontier';
import { PostgresPluginPackageWorkflowTaskAttemptAdmissionRepository } from '@qinglong/cluster-postgres/plugin-package-workflow-task-attempt-admission';
import { PostgresTaskStartRepository } from '@qinglong/cluster-postgres/task-start';
import { PostgresPluginPackageAutomationPublicationRepository } from '@qinglong/cluster-postgres/plugin-package-automation-publication';
import { PostgresPluginPackageMaterializedRevisionRepository } from '@qinglong/cluster-postgres/plugin-package-materialized-revision';
import {
PostgresAuthorizedPluginPackageWorkflowAdmissionRepository,
PostgresAuthorizedPluginPackageWorkflowRunEventListRepository,
PostgresAuthorizedPluginPackageWorkflowRunInspectionRepository,
PostgresAuthorizedPluginPackageWorkflowRunListRepository,
PostgresAuthorizedPluginPackageWorkflowStepRunListRepository,
} from '@qinglong/cluster-postgres/plugin-package-workflow-administration';
import {
assertClusterControlApiCredentialPepper,
createClusterControlApiCredentialAuthenticator,
} from '../authentication/apiCredentialAuthenticator';
import type {
ClusterControlRequestAuthenticator,
ClusterControlSecurityAuditSink,
} from '../transport/admissionPipeline';
import {
MAX_CLUSTER_SCHEDULE_CLAIMS_PER_CYCLE,
ClusterSchedulerCoordinator,
ClusterSchedulerLifecycle,
type ClusterSchedulerCycleSummary,
} from '../scheduling/scheduler';
import { ClusterWorkflowSchedulerCoordinator } from '../scheduling/workflowScheduler';
import { ClusterRuntimeSchedulerCoordinator } from '../scheduling/runtimeScheduler';
import { ClusterRunCancellationConvergenceLifecycle } from '../run/runCancellationLifecycle';
import type { TaskStartRepository } from '@qinglong/runtime-core/task-start';
import {
createClusterWorkerRuntimePort,
type ClusterWorkerRuntimeDependencies,
type ClusterWorkerRuntimePort,
} from '../remote-execution/workerRuntimePort';
import {
createClusterPluginPackageWorkflowAdministrationCapability,
type ClusterPluginPackageWorkflowAdministrationCapability,
} from '../plugin-package/workflow/pluginPackageWorkflowAdministration';
export interface ClusterTrustedToolStorage {
readonly invocationArtifacts: ToolInvocationArtifactRepository;
readonly stepRuns: StepRunRepository;
readonly startBarriers: ToolExecutionStartBarrierRepository;
readonly completions: ToolExecutionCompletionRepository;
readonly failureCompletions: ToolExecutionFailureCompletionRepository;
readonly resultKeyCatalog: ToolResultKeyCatalogReader;
readonly resultRekeys: ToolExecutionResultRekeyReader;
readonly toolDefinitionSnapshots: ProjectToolDefinitionSnapshotRepository &
ProjectToolDefinitionSnapshotSourceRepository;
}
export interface ClusterControlAssemblyInput {
readonly evidence: ClusterControlReadinessEvidence;
readonly policies: ProjectPolicyRepository;
readonly runs: RunRepository & ProjectRunListReader;
readonly runCancellation: ClusterRunCancellationRepository;
readonly taskStart: TaskStartRepository;
readonly taskDefinitions: TaskDefinitionSource;
readonly taskExecutionRevisions: ClusterTaskExecutionRevisionSource;
readonly triggers: TriggerSource;
readonly schedules: ClusterScheduleStore;
readonly trustedToolStorage: ClusterTrustedToolStorage;
readonly authenticator: ClusterControlRequestAuthenticator;
readonly securityAudit: ClusterControlSecurityAuditSink;
readonly workflowAdministration: ClusterPluginPackageWorkflowAdministrationCapability;
readonly workerRuntime?: ClusterWorkerRuntimePort;
}
export interface ClusterControlRecoveryRuntimeOptions {
readonly ownerId: string;
readonly providers?: readonly ClusterControlRecoveryExecutorEvidenceProvider[];
readonly claimLimit?: number;
readonly claimLeaseMs?: number;
readonly retryDelayMs?: number;
readonly providerTimeoutMs?: number;
readonly maxStartupPasses?: number;
}
export interface ClusterSchedulerRuntimeOptions {
readonly ownerId?: string;
readonly claimLeaseMs?: number;
readonly maxClaimsPerCycle?: number;
readonly misfireGraceMs?: number;
readonly intervalMs?: number;
readonly stopTimeoutMs?: number;
readonly onDiagnostic?: (
error: unknown,
summary?: ClusterSchedulerCycleSummary,
) => void | Promise<void>;
}
export interface ClusterRunCancellationConvergenceRuntimeOptions {
readonly pageSize?: number;
readonly maxPages?: number;
readonly intervalMs?: number;
readonly stopTimeoutMs?: number;
readonly onDiagnostic?: (
error: unknown,
summary?: Readonly<ClusterRunCancellationConvergenceCycleResult>,
) => void | Promise<void>;
}
export interface ClusterControlBootstrapOptions {
readonly enabled?: boolean;
readonly profile: DeploymentProfile;
readonly apiCredentialPepper?: string;
readonly recovery?: ClusterControlRecoveryRuntimeOptions;
readonly scheduler?: ClusterSchedulerRuntimeOptions;
readonly cancellationConvergence?: ClusterRunCancellationConvergenceRuntimeOptions;
readonly workerRuntime?: ClusterWorkerRuntimeDependencies;
readonly openDatabase: OpenPostgresDatabase;
readonly create: (
input: ClusterControlAssemblyInput,
) => ClusterControlActivationStack;
readonly audit: (
record: ClusterControlActivationAudit,
) => void | Promise<void>;
}
interface PreparedRecoveryRuntime {
readonly providers: readonly ClusterControlRecoveryExecutorEvidenceProvider[];
readonly providerTimeoutMs: number;
readonly ownerId: string;
readonly claimLimit: number;
readonly claimLeaseMs: number;
readonly retryDelayMs: number;
readonly maxStartupPasses: number;
}
interface PreparedSchedulerRuntime {
readonly ownerId: string;
readonly claimLeaseMs: number;
readonly maxClaimsPerCycle: number;
readonly misfireGraceMs: number;
readonly intervalMs: number;
readonly stopTimeoutMs: number;
readonly onDiagnostic?: ClusterSchedulerRuntimeOptions['onDiagnostic'];
}
interface PreparedCancellationConvergenceRuntime {
readonly pageSize: number;
readonly maxPages: number;
readonly intervalMs: number;
readonly stopTimeoutMs: number;
readonly onDiagnostic?: ClusterRunCancellationConvergenceRuntimeOptions['onDiagnostic'];
}
function boundedInteger(
name: string,
value: number | undefined,
fallback: number,
minimum: number,
maximum: number,
): number {
const normalized = value ?? fallback;
if (
!Number.isSafeInteger(normalized) ||
normalized < minimum ||
normalized > maximum
) {
throw new RangeError(`${name} must be between ${minimum} and ${maximum}`);
}
return normalized;
}
function prepareRecoveryRuntime(
options: ClusterControlRecoveryRuntimeOptions | undefined,
): PreparedRecoveryRuntime {
if (!options) {
throw new TypeError(
'Enabled cluster-control requires bounded recovery configuration',
);
}
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(options.ownerId)) {
throw new TypeError('Cluster-control recovery ownerId is invalid');
}
const claimLeaseMs = boundedInteger(
'Cluster-control recovery claim lease',
options.claimLeaseMs,
30_000,
1_000,
MAX_CLUSTER_CONTROL_RECOVERY_CLAIM_LEASE_MS,
);
const providerTimeoutMs = boundedInteger(
'Cluster-control recovery evidence timeout',
options.providerTimeoutMs,
5_000,
1,
MAX_CLUSTER_CONTROL_RECOVERY_EVIDENCE_TIMEOUT_MS,
);
if (providerTimeoutMs + 250 > claimLeaseMs) {
throw new RangeError(
'Cluster-control recovery evidence timeout must leave at least 250ms for fenced settlement',
);
}
return Object.freeze({
providers: Object.freeze([...(options.providers ?? [])]),
providerTimeoutMs,
ownerId: options.ownerId,
claimLimit: boundedInteger(
'Cluster-control recovery claim limit',
options.claimLimit,
16,
1,
MAX_CLUSTER_CONTROL_RECOVERY_CLAIMS_PER_PASS,
),
claimLeaseMs,
retryDelayMs: boundedInteger(
'Cluster-control recovery retry delay',
options.retryDelayMs,
5_000,
0,
MAX_CLUSTER_CONTROL_RECOVERY_RETRY_DELAY_MS,
),
maxStartupPasses: boundedInteger(
'Cluster-control startup recovery passes',
options.maxStartupPasses,
8,
1,
MAX_CLUSTER_CONTROL_STARTUP_RECOVERY_PASSES,
),
});
}
function prepareSchedulerRuntime(
options: ClusterSchedulerRuntimeOptions | undefined,
fallbackOwnerId: string,
): PreparedSchedulerRuntime {
const allowedKeys = new Set([
'claimLeaseMs',
'intervalMs',
'maxClaimsPerCycle',
'misfireGraceMs',
'onDiagnostic',
'ownerId',
'stopTimeoutMs',
]);
if (
options !== undefined &&
(!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
Object.keys(options).some((key) => !allowedKeys.has(key)))
) {
throw new TypeError('Cluster scheduler configuration is invalid');
}
const ownerId = options?.ownerId ?? fallbackOwnerId;
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(ownerId)) {
throw new TypeError('Cluster scheduler ownerId is invalid');
}
if (
options?.onDiagnostic !== undefined &&
typeof options.onDiagnostic !== 'function'
) {
throw new TypeError('Cluster scheduler diagnostic sink is invalid');
}
return Object.freeze({
ownerId,
claimLeaseMs: boundedInteger(
'Cluster scheduler claim lease',
options?.claimLeaseMs,
30_000,
1_000,
60_000,
),
maxClaimsPerCycle: boundedInteger(
'Cluster scheduler claim budget',
options?.maxClaimsPerCycle,
16,
1,
MAX_CLUSTER_SCHEDULE_CLAIMS_PER_CYCLE,
),
misfireGraceMs: boundedInteger(
'Cluster scheduler misfire grace',
options?.misfireGraceMs,
30_000,
0,
5 * 60_000,
),
intervalMs: boundedInteger(
'Cluster scheduler interval',
options?.intervalMs,
1_000,
250,
60 * 60_000,
),
stopTimeoutMs: boundedInteger(
'Cluster scheduler stop timeout',
options?.stopTimeoutMs,
10_000,
100,
30_000,
),
...(options?.onDiagnostic === undefined
? {}
: { onDiagnostic: options.onDiagnostic }),
});
}
function prepareCancellationConvergenceRuntime(
options: ClusterRunCancellationConvergenceRuntimeOptions | undefined,
): PreparedCancellationConvergenceRuntime {
const allowedKeys = new Set([
'intervalMs',
'maxPages',
'onDiagnostic',
'pageSize',
'stopTimeoutMs',
]);
if (
options !== undefined &&
(!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
Object.keys(options).some((key) => !allowedKeys.has(key)))
) {
throw new TypeError(
'Cluster Run cancellation convergence configuration is invalid',
);
}
if (
options?.onDiagnostic !== undefined &&
typeof options.onDiagnostic !== 'function'
) {
throw new TypeError(
'Cluster Run cancellation convergence diagnostic sink is invalid',
);
}
return Object.freeze({
pageSize: boundedInteger(
'Cluster Run cancellation convergence page size',
options?.pageSize,
32,
1,
MAX_CLUSTER_RUN_CANCELLATION_CONVERGENCE_PAGE_SIZE,
),
maxPages: boundedInteger(
'Cluster Run cancellation convergence page limit',
options?.maxPages,
4,
1,
MAX_CLUSTER_RUN_CANCELLATION_CONVERGENCE_PAGES_PER_CYCLE,
),
intervalMs: boundedInteger(
'Cluster Run cancellation convergence interval',
options?.intervalMs,
1_000,
250,
60 * 60_000,
),
stopTimeoutMs: boundedInteger(
'Cluster Run cancellation convergence stop timeout',
options?.stopTimeoutMs,
10_000,
100,
30_000,
),
...(options?.onDiagnostic === undefined
? {}
: { onDiagnostic: options.onDiagnostic }),
});
}
function readinessEvidence(
report: PostgresSchemaReadinessReport,
): ClusterControlReadinessEvidence {
return Object.freeze({
contractName: report.contractName,
contractVersion: report.contractVersion,
serverMajor: report.serverMajor,
migrationIds: Object.freeze([...report.migrationIds]),
});
}
/**
* Owns the cluster database around the readiness-first activation gate.
* Repository and service construction happens through create() only after the
* runtime role, migration history and catalog contract are proven ready.
*/
export async function bootstrapClusterControlRuntime(
options: ClusterControlBootstrapOptions,
): Promise<ClusterControlRuntimeActivationResult> {
let recoveryRuntime: PreparedRecoveryRuntime | undefined;
let schedulerRuntime: PreparedSchedulerRuntime | undefined;
let cancellationConvergenceRuntime:
| PreparedCancellationConvergenceRuntime
| undefined;
let recoveryRegistry: ClusterControlRecoveryEvidenceRegistry | undefined;
if ((options.enabled ?? false) && options.profile === 'cluster-control') {
assertClusterControlApiCredentialPepper(options.apiCredentialPepper ?? '');
recoveryRuntime = prepareRecoveryRuntime(options.recovery);
schedulerRuntime = prepareSchedulerRuntime(
options.scheduler,
recoveryRuntime.ownerId,
);
cancellationConvergenceRuntime = prepareCancellationConvergenceRuntime(
options.cancellationConvergence,
);
}
let database: PostgresDatabaseResource | undefined;
let closePromise: Promise<void> | undefined;
const closeDatabase = (): Promise<void> => {
if (!database) return Promise.resolve();
closePromise ??= Promise.resolve().then(() => database!.close());
return closePromise;
};
try {
const activation = await activateClusterControlRuntime({
...(options.enabled === undefined ? {} : { enabled: options.enabled }),
profile: options.profile,
readiness: {
async assertReady() {
if (database) {
throw new Error(
'Cluster-control database was opened more than once',
);
}
database = await options.openDatabase();
return readinessEvidence(
await assertPostgresSchemaReady(database.pool),
);
},
},
create(evidence) {
if (!database) {
throw new Error(
'Cluster-control database is unavailable after readiness',
);
}
const recovery = new PostgresClusterControlRecoverySource(
database.pool,
);
const recoveryClaims =
new PostgresClusterControlRecoveryClaimRepository(database.pool);
const recoveryTransitions =
new PostgresClusterControlRecoveryResolutionRepository(database.pool);
if (!recoveryRuntime) {
throw new Error(
'Cluster-control recovery runtime is unavailable after readiness',
);
}
if (!schedulerRuntime) {
throw new Error(
'Cluster scheduler runtime is unavailable after readiness',
);
}
if (!cancellationConvergenceRuntime) {
throw new Error(
'Cluster Run cancellation convergence runtime is unavailable after readiness',
);
}
const remoteAttestations =
new PostgresWorkerExecutionAttestationRepository(database.pool);
recoveryRegistry = new ClusterControlRecoveryEvidenceRegistry(
[
...recoveryRuntime.providers,
new PostgresRemoteWorkerAttestationEvidenceProvider(
database.pool,
remoteAttestations,
),
],
{ timeoutMs: recoveryRuntime.providerTimeoutMs },
);
const recoveryProcessor =
new EvidenceBasedClusterControlRecoveryProcessor(
recoveryTransitions,
recoveryRegistry,
{ retryDelayMs: recoveryRuntime.retryDelayMs },
);
const recoverySupervisor = new ClusterControlRecoverySupervisor(
recoveryClaims,
recoveryProcessor,
{
ownerId: recoveryRuntime.ownerId,
limit: recoveryRuntime.claimLimit,
leaseMs: recoveryRuntime.claimLeaseMs,
retryDelayMs: recoveryRuntime.retryDelayMs,
},
);
const recoveryCoordinator =
new ClusterControlStartupRecoveryCoordinator(recoverySupervisor, {
maxPasses: recoveryRuntime.maxStartupPasses,
});
const runtimeRecoverySupervisor = new ClusterControlRecoverySupervisor(
new PostgresClusterControlRecoveryClaimRepository(
database.pool,
randomUUID,
(queryable) => new PostgresClusterRuntimeRecoverySource(queryable),
),
recoveryProcessor,
{
ownerId: recoveryRuntime.ownerId,
limit: recoveryRuntime.claimLimit,
leaseMs: recoveryRuntime.claimLeaseMs,
retryDelayMs: recoveryRuntime.retryDelayMs,
},
);
const schedules = new PostgresClusterScheduleRepository(database.pool);
const runs = new PostgresRunRepository(database.pool);
const trustedToolStorage: ClusterTrustedToolStorage = Object.freeze({
invocationArtifacts: new PostgresToolInvocationArtifactRepository(
database.pool,
),
stepRuns: new PostgresStepRunRepository(database.pool),
startBarriers: new PostgresToolExecutionStartBarrierRepository(
database.pool,
),
completions: new PostgresToolExecutionCompletionRepository(
database.pool,
),
failureCompletions:
new PostgresToolExecutionFailureCompletionRepository(database.pool),
resultKeyCatalog: new PostgresToolResultKeyCatalogReader(
database.pool,
),
resultRekeys: new PostgresToolResultRekeyReader(database.pool),
toolDefinitionSnapshots:
new PostgresProjectToolDefinitionSnapshotRepository(database.pool),
});
const workflowScheduler = new ClusterWorkflowSchedulerCoordinator(
new ClusterSchedulerCoordinator(schedules, {
ownerId: schedulerRuntime.ownerId,
claimLeaseMs: schedulerRuntime.claimLeaseMs,
maxClaimsPerCycle: schedulerRuntime.maxClaimsPerCycle,
misfireGraceMs: schedulerRuntime.misfireGraceMs,
}),
new PostgresPluginPackageWorkflowFrontierRepository(database.pool),
new PostgresPluginPackageWorkflowTaskAttemptAdmissionRepository(
database.pool,
),
{
frontierPageSize: 32,
frontierMaxPages: 4,
taskAttemptPageSize: 32,
taskAttemptMaxPages: 4,
},
);
const runtimeScheduler = new ClusterRuntimeSchedulerCoordinator(
runtimeRecoverySupervisor,
new ClusterRunLostRetryCoordinator(
new PostgresClusterRunLostRetryRepository(database.pool),
{ pageSize: 16 },
),
workflowScheduler,
);
const schedulerLifecycle = new ClusterSchedulerLifecycle(
runtimeScheduler,
{
intervalMs: schedulerRuntime.intervalMs,
stopTimeoutMs: schedulerRuntime.stopTimeoutMs,
...(schedulerRuntime.onDiagnostic === undefined
? {}
: { onDiagnostic: schedulerRuntime.onDiagnostic }),
},
);
const cancellationConvergenceLifecycle =
new ClusterRunCancellationConvergenceLifecycle(
new ClusterRunCancellationConvergenceCoordinator(
new PostgresClusterRunCancellationConvergenceRepository(
database.pool,
),
{
pageSize: cancellationConvergenceRuntime.pageSize,
maxPages: cancellationConvergenceRuntime.maxPages,
},
),
{
intervalMs: cancellationConvergenceRuntime.intervalMs,
stopTimeoutMs: cancellationConvergenceRuntime.stopTimeoutMs,
...(cancellationConvergenceRuntime.onDiagnostic === undefined
? {}
: {
onDiagnostic: cancellationConvergenceRuntime.onDiagnostic,
}),
},
);
const runCancellation = new PostgresClusterRunCancellationRepository(
database.pool,
);
const taskStart = new PostgresTaskStartRepository(database.pool);
const application = options.create({
evidence,
authenticator: createClusterControlApiCredentialAuthenticator(
new PostgresApiCredentialRepository(database.pool),
options.apiCredentialPepper ?? '',
),
policies: new PostgresProjectPolicyRepository(database.pool),
runs,
runCancellation,
taskStart,
taskDefinitions: new PostgresTaskDefinitionSource(database.pool),
taskExecutionRevisions: new PostgresTaskExecutionRevisionSource(
database.pool,
),
triggers: new PostgresTriggerSource(database.pool),
schedules,
trustedToolStorage,
securityAudit: new PostgresSecurityAuditRepository(database.pool),
workflowAdministration:
createClusterPluginPackageWorkflowAdministrationCapability(
new PostgresPluginPackageAutomationPublicationRepository(
database.pool,
),
new PostgresPluginPackageMaterializedRevisionRepository(
database.pool,
),
new PostgresAuthorizedPluginPackageWorkflowAdmissionRepository(
database.pool,
),
new PostgresAuthorizedPluginPackageWorkflowRunInspectionRepository(
database.pool,
),
new PostgresAuthorizedPluginPackageWorkflowRunListRepository(
database.pool,
),
new PostgresAuthorizedPluginPackageWorkflowStepRunListRepository(
database.pool,
),
new PostgresAuthorizedPluginPackageWorkflowRunEventListRepository(
database.pool,
),
runCancellation,
),
...(options.workerRuntime === undefined
? {}
: {
workerRuntime: createClusterWorkerRuntimePort(
database.pool,
options.workerRuntime,
),
}),
});
const convergence = new ClusterControlRecoveryConvergenceVerifier(
recovery,
);
return {
async reconcile() {
const outstanding = await convergence.verify();
if (!outstanding.safe) {
const system = await recoveryCoordinator.reconcile();
if (
!system.safe ||
system.remaining !== 0 ||
system.failed !== 0
) {
return system;
}
}
const summary = await application.reconcile();
if (
!summary.safe ||
summary.remaining !== 0 ||
summary.failed !== 0
) {
return summary;
}
return convergence.verify();
},
async startLifecycles() {
if (!(await application.startLifecycles())) return false;
schedulerLifecycle.start();
cancellationConvergenceLifecycle.start();
return true;
},
installAdmission: () => application.installAdmission(),
async stop() {
recoveryRegistry?.dispose();
let schedulerStatus: 'stopped' | 'timed_out' = 'stopped';
let cancellationStatus: 'stopped' | 'timed_out' = 'stopped';
let applicationStatus: ClusterControlStopResult = 'stopped';
let primaryError: unknown;
try {
cancellationStatus = (
await cancellationConvergenceLifecycle.stopAndDrain()
).status;
} catch (error) {
primaryError = error;
}
try {
schedulerStatus = (await schedulerLifecycle.stopAndDrain())
.status;
} catch (error) {
primaryError ??= error;
}
try {
applicationStatus = await application.stop();
} catch (error) {
primaryError ??= error;
}
if (primaryError) throw primaryError;
return cancellationStatus === 'timed_out' ||
schedulerStatus === 'timed_out' ||
applicationStatus === 'timed_out'
? 'timed_out'
: 'stopped';
},
};
},
audit: options.audit,
});
if (activation.status === 'disabled') return activation;
let stopPromise: Promise<ClusterControlStopResult> | undefined;
return {
...activation,
stop() {
if (stopPromise) return stopPromise;
stopPromise = (async () => {
let result: ClusterControlStopResult | undefined;
let primaryError: unknown;
try {
result = await activation.stop();
} catch (error) {
primaryError = error;
}
try {
await closeDatabase();
} catch (error) {
primaryError ??= error;
}
if (primaryError) throw primaryError;
return result!;
})();
return stopPromise;
},
};
} catch (error) {
recoveryRegistry?.dispose();
try {
await closeDatabase();
} catch {
// Preserve the readiness/assembly/activation failure.
}
throw error;
}
}
export type {
ClusterControlActivationAudit,
ClusterControlActivationStack,
ClusterControlReadinessEvidence,
ClusterControlRuntimeActivationResult,
ClusterControlStopResult,
DeploymentProfile,
OpenPostgresDatabase,
PostgresDatabaseResource,
PostgresPool,
ProjectPolicyRepository,
RunRepository,
ClusterRunCancellationRepository,
ClusterControlRequestAuthenticator,
ClusterControlSecurityAuditSink,
ClusterControlRecoveryExecutorEvidenceProvider,
ClusterScheduleStore,
};
export { ClusterRunCancellationConvergenceLifecycle } from '../run/runCancellationLifecycle';
export * from '../scheduling/scheduler';
export * from '../scheduling/workflowScheduler';
export * from '../scheduling/runtimeScheduler';
export * from '../remote-execution/remoteWorkerDispatcher';
export * from '../remote-execution/workerRuntimePort';
@@ -0,0 +1,410 @@
import { randomUUID } from 'node:crypto';
import type {
ClusterControlStartupRecoverySummary,
ClusterControlStopResult,
} from '@qinglong/runtime-core';
import type { RemoteWorkerSecretValueProvider } from '@qinglong/runtime-core/remote-secret-delivery';
import {
startClusterControlApplication,
type ClusterControlApplicationOptions,
type ClusterControlApplicationResult,
type ClusterControlApplicationStack,
} from './application';
import {
createClusterControlDatabaseBinding,
type EnabledClusterControlConfig,
} from '../production-process/config';
import {
createClusterControlAdmissionPipeline,
createClusterControlProjectPolicyAuthorizer,
} from '../transport/admissionPipeline';
import { createClusterControlRouteRegistry } from '../transport/routeRegistry';
import { CLUSTER_CONTROL_HTTP_DEFAULTS } from '../transport/httpSurface';
import { createClusterControlRunReadRoute } from '../run/runReadRoute';
import { createClusterControlRunListRoute } from '../run/runListRoute';
import { createClusterControlRunEventListRoute } from '../run/runEventListRoute';
import { createClusterControlRunStepListRoute } from '../run/runStepListRoute';
import { createClusterControlTaskListRoute } from '../task/taskListRoute';
import { createClusterControlTaskReadRoute } from '../task/taskReadRoute';
import { createClusterControlTaskStartRoute } from '../task/taskStartRoute';
import {
createClusterControlPluginPackagePromptExecutionRoute,
type ClusterPluginPackagePromptExecutionCapability,
} from '../plugin-package/prompt/pluginPackagePromptExecutionRoute';
import {
createClusterControlPluginPackagePromptCatalogRoute,
type ClusterPluginPackagePromptCatalogCapability,
} from '../plugin-package/prompt/pluginPackagePromptCatalogRoute';
import {
createClusterControlPluginPackagePromptOutputReadRoute,
type ClusterPluginPackagePromptOutputReadCapability,
} from '../plugin-package/prompt/pluginPackagePromptOutputReadRoute';
import {
createClusterControlPluginPackagePromptExecutionInspectionRoute,
type ClusterPluginPackagePromptExecutionInspectionCapability,
} from '../plugin-package/prompt/pluginPackagePromptExecutionInspectionRoute';
import {
createClusterControlPluginPackagePromptExecutionOutputReadRoute,
type ClusterPluginPackagePromptExecutionOutputReadCapability,
} from '../plugin-package/prompt/pluginPackagePromptExecutionOutputReadRoute';
import {
createClusterControlRunCancellationRoute,
type ClusterRunCancellationEventIdFactory,
} from '../run/runCancellationRoute';
import type { ClusterControlAssemblyInput } from './clusterControlRuntime';
import type { ClusterRemoteWorkerArtifactStore } from '../remote-execution/remoteWorkerCompletionService';
import type { EnabledClusterWorkerIngressConfig } from '../worker-ingress/workerIngressConfig';
import {
startProductionClusterWorkerIngress,
type ProductionClusterWorkerIngressOptions as ProductionClusterWorkerIngressStarterOptions,
} from '../worker-ingress/productionWorkerIngress';
import type { ClusterWorkerIngressApplicationResult } from '../worker-ingress/workerIngressApplication';
import { createClusterControlPluginPackageWorkflowRoutes } from '../plugin-package/workflow/pluginPackageWorkflowRoute';
export const PRODUCTION_CLUSTER_CONTROL_ROUTE_OPERATIONS = Object.freeze([
'task.get',
'task.list',
'task.start',
'run.get',
'run.list',
'run.events.list',
'run.steps.list',
'run.cancel',
'workflow.read',
'workflow.run.read',
'workflow.run.list',
'workflow.step.list',
'workflow.event.list',
'workflow.start',
'workflow.cancel',
] as const);
export const PRODUCTION_CLUSTER_CONTROL_OPTIONAL_ROUTE_OPERATIONS =
Object.freeze([
'prompt.read',
'prompt.execute',
'prompt.execution.read',
'prompt.execution.output.read',
'prompt.output.read',
] as const);
export interface ProductionClusterControlAssemblyOptions {
readonly createEventId?: ClusterRunCancellationEventIdFactory;
readonly promptCatalog?: Readonly<{
readonly capability: ClusterPluginPackagePromptCatalogCapability;
}>;
readonly promptExecution?: Readonly<{
readonly capability: ClusterPluginPackagePromptExecutionCapability;
readonly maxExecutionMs?: number;
readonly now?: () => number;
}>;
readonly promptExecutionInspection?: Readonly<{
readonly capability: ClusterPluginPackagePromptExecutionInspectionCapability;
readonly now?: () => number;
}>;
readonly promptOutputRead?: Readonly<{
readonly capability: ClusterPluginPackagePromptOutputReadCapability;
}>;
readonly promptExecutionOutputRead?: Readonly<{
readonly capability: ClusterPluginPackagePromptExecutionOutputReadCapability;
}>;
readonly workerIngress?: Readonly<{
readonly config: EnabledClusterWorkerIngressConfig;
readonly onDiagnostic?: (error: unknown) => void | Promise<void>;
}>;
readonly startWorkerIngress?: (
options: ProductionClusterWorkerIngressStarterOptions,
) => Promise<ClusterWorkerIngressApplicationResult>;
}
export interface ProductionClusterWorkerIngressOptions {
readonly config: EnabledClusterWorkerIngressConfig;
readonly artifactStore: ClusterRemoteWorkerArtifactStore;
readonly secretProvider?: RemoteWorkerSecretValueProvider;
readonly onDiagnostic?: (error: unknown) => void | Promise<void>;
}
export interface ProductionClusterControlApplicationOptions
extends Omit<
ClusterControlApplicationOptions,
| 'create'
| 'enabled'
| 'profile'
| 'apiCredentialPepper'
| 'openDatabase'
| 'availability'
| 'http'
| 'workerRuntime'
> {
readonly config: EnabledClusterControlConfig;
readonly createEventId?: ClusterRunCancellationEventIdFactory;
readonly promptCatalog?: Readonly<{
readonly capability: ClusterPluginPackagePromptCatalogCapability;
}>;
readonly promptExecution?: Readonly<{
readonly capability: ClusterPluginPackagePromptExecutionCapability;
}>;
readonly promptExecutionInspection?: Readonly<{
readonly capability: ClusterPluginPackagePromptExecutionInspectionCapability;
}>;
readonly promptOutputRead?: Readonly<{
readonly capability: ClusterPluginPackagePromptOutputReadCapability;
}>;
readonly promptExecutionOutputRead?: Readonly<{
readonly capability: ClusterPluginPackagePromptExecutionOutputReadCapability;
}>;
readonly workerIngress?: ProductionClusterWorkerIngressOptions;
}
const SAFE_RECOVERY: Readonly<ClusterControlStartupRecoverySummary> =
Object.freeze({ safe: true, remaining: 0, failed: 0 });
function eventIdFactory(
candidate: ClusterRunCancellationEventIdFactory | undefined,
): ClusterRunCancellationEventIdFactory {
if (candidate !== undefined && typeof candidate !== 'function') {
throw new TypeError(
'Production cluster-control event ID factory is invalid',
);
}
return candidate ?? randomUUID;
}
/**
* The reviewed production business surface. Bootstrap still owns PostgreSQL,
* startup recovery, the scheduler and cancellation convergence; this stack
* owns only the exact route allowlist and its admission pipeline.
*/
export function createProductionClusterControlApplicationStack(
input: ClusterControlAssemblyInput,
options: ProductionClusterControlAssemblyOptions = {},
): ClusterControlApplicationStack {
const createEventId = eventIdFactory(options.createEventId);
const workerIngressStarter =
options.startWorkerIngress ?? startProductionClusterWorkerIngress;
if (typeof workerIngressStarter !== 'function') {
throw new TypeError('Production Worker ingress starter is invalid');
}
const routeDefinitions = [
createClusterControlTaskReadRoute(input.taskDefinitions),
createClusterControlTaskListRoute(input.taskDefinitions),
createClusterControlTaskStartRoute(input.taskStart, createEventId),
createClusterControlRunReadRoute(input.runs),
createClusterControlRunListRoute(input.runs),
createClusterControlRunEventListRoute(input.runs),
createClusterControlRunStepListRoute(
input.runs,
input.trustedToolStorage.stepRuns,
),
createClusterControlRunCancellationRoute(
input.runCancellation,
createEventId,
),
...createClusterControlPluginPackageWorkflowRoutes(
input.workflowAdministration,
Date.now,
createEventId,
),
...(options.promptCatalog === undefined
? []
: [
createClusterControlPluginPackagePromptCatalogRoute(
options.promptCatalog.capability,
),
]),
...(options.promptExecution === undefined
? []
: [
createClusterControlPluginPackagePromptExecutionRoute(
options.promptExecution.capability,
{
...(options.promptExecution.maxExecutionMs === undefined
? {}
: { maxExecutionMs: options.promptExecution.maxExecutionMs }),
...(options.promptExecution.now === undefined
? {}
: { now: options.promptExecution.now }),
createEventId,
},
),
]),
...(options.promptExecutionInspection === undefined
? []
: [
createClusterControlPluginPackagePromptExecutionInspectionRoute(
options.promptExecutionInspection.capability,
{
...(options.promptExecutionInspection.now === undefined
? {}
: { now: options.promptExecutionInspection.now }),
createEventId,
},
),
]),
...(options.promptOutputRead === undefined
? []
: [
createClusterControlPluginPackagePromptOutputReadRoute(
options.promptOutputRead.capability,
),
]),
...(options.promptExecutionOutputRead === undefined
? []
: [
createClusterControlPluginPackagePromptExecutionOutputReadRoute(
options.promptExecutionOutputRead.capability,
),
]),
];
const routes = createClusterControlRouteRegistry(routeDefinitions);
const expectedRouteCount =
PRODUCTION_CLUSTER_CONTROL_ROUTE_OPERATIONS.length +
(options.promptCatalog === undefined ? 0 : 1) +
(options.promptExecution === undefined ? 0 : 1) +
(options.promptExecutionInspection === undefined ? 0 : 1) +
(options.promptOutputRead === undefined ? 0 : 1) +
(options.promptExecutionOutputRead === undefined ? 0 : 1);
if (routes.size !== expectedRouteCount) {
throw new Error('Production cluster-control route allowlist is incomplete');
}
const admission = createClusterControlAdmissionPipeline({
routes,
authenticator: input.authenticator,
policy: createClusterControlProjectPolicyAuthorizer(input.policies),
audit: input.securityAudit,
});
let workerIngress:
| Extract<ClusterWorkerIngressApplicationResult, { status: 'active' }>
| undefined;
let workerIngressStart:
| Promise<
Extract<ClusterWorkerIngressApplicationResult, { status: 'active' }>
>
| undefined;
let workerIngressUnavailable: unknown;
let workerIngressStop: Promise<'stopped'> | undefined;
const stopWorkerIngress = (): Promise<'stopped'> => {
if (!workerIngress) return Promise.resolve('stopped' as const);
workerIngressStop ??= workerIngress.stop();
return workerIngressStop;
};
const startWorkerIngress = async (): Promise<void> => {
const ingress = options.workerIngress;
if (!ingress) return;
if (!input.workerRuntime) {
throw new Error(
'Production Worker ingress requires an injected runtime service port',
);
}
workerIngressStart ??= (async () => {
const result = await workerIngressStarter({
config: ingress.config,
runtime: input.workerRuntime!,
onPoolError(error) {
workerIngressUnavailable ??= error;
void Promise.resolve(ingress.onDiagnostic?.(error)).catch(
() => undefined,
);
void stopWorkerIngress().catch(() => undefined);
},
});
if (result.status !== 'active') {
throw new Error('Production Worker ingress did not activate');
}
workerIngress = result;
if (workerIngressUnavailable !== undefined) {
await stopWorkerIngress();
throw new Error(
'Production Worker ingress database became unavailable during activation',
);
}
return result;
})();
await workerIngressStart;
};
return Object.freeze({
async reconcile(): Promise<ClusterControlStartupRecoverySummary> {
return SAFE_RECOVERY;
},
async startLifecycles(): Promise<boolean> {
await startWorkerIngress();
return true;
},
admission,
async stop(): Promise<ClusterControlStopResult> {
await stopWorkerIngress();
return 'stopped';
},
});
}
/** Starts cluster-control with the reviewed production route allowlist. */
export function startProductionClusterControlApplication(
options: ProductionClusterControlApplicationOptions,
): Promise<ClusterControlApplicationResult> {
const createEventId = eventIdFactory(options.createEventId);
const {
createEventId: _ignoredCreateEventId,
config,
workerIngress,
promptCatalog,
promptExecution,
promptExecutionInspection,
promptOutputRead,
promptExecutionOutputRead,
...applicationOptions
} = options;
const database = createClusterControlDatabaseBinding(config);
return startClusterControlApplication({
...applicationOptions,
enabled: true,
profile: 'cluster-control',
apiCredentialPepper: config.security.apiCredentialPepper,
http: config.http,
...(workerIngress === undefined
? {}
: {
workerRuntime: {
artifactStore: workerIngress.artifactStore,
...(workerIngress.secretProvider === undefined
? {}
: { secretProvider: workerIngress.secretProvider }),
},
}),
...database,
create: (input) =>
createProductionClusterControlApplicationStack(input, {
createEventId,
...(promptCatalog === undefined ? {} : { promptCatalog }),
...(promptExecution === undefined
? {}
: {
promptExecution: {
capability: promptExecution.capability,
maxExecutionMs: Math.max(
1,
(config.http.requestTimeoutMs ??
CLUSTER_CONTROL_HTTP_DEFAULTS.requestTimeoutMs) - 100,
),
},
}),
...(promptExecutionInspection === undefined
? {}
: { promptExecutionInspection }),
...(promptOutputRead === undefined ? {} : { promptOutputRead }),
...(promptExecutionOutputRead === undefined
? {}
: { promptExecutionOutputRead }),
...(workerIngress === undefined
? {}
: {
workerIngress: {
config: workerIngress.config,
...(workerIngress.onDiagnostic === undefined
? {}
: { onDiagnostic: workerIngress.onDiagnostic }),
},
}),
}),
});
}
@@ -0,0 +1,778 @@
// Artifact owns immutable S3 evidence, checksum validation, and conditional promotion.
import { createHash, randomBytes, randomUUID } from 'node:crypto';
import { Readable } from 'node:stream';
import {
ChecksumAlgorithm,
ChecksumMode,
CopyObjectCommand,
DeleteObjectCommand,
HeadObjectCommand,
MetadataDirective,
PutObjectCommand,
S3Client,
ServerSideEncryption,
} from '@aws-sdk/client-s3';
import {
MAX_REMOTE_WORKER_ARTIFACT_BYTES,
REMOTE_WORKER_ARTIFACT_CONTENT_TYPE,
normalizeRemoteWorkerArtifactReceipt,
type RemoteWorkerArtifactReceipt,
} from '@qinglong/runtime-core/remote-worker-completion';
import type {
ClusterRemoteWorkerArtifactLookup,
ClusterRemoteWorkerArtifactStorageCommand,
ClusterRemoteWorkerArtifactStore,
} from '../remote-execution/remoteWorkerCompletionService';
const DEFAULT_PREFIX = 'qinglong/v3/worker-artifacts';
const METADATA_SCHEMA = 'qinglong-remote-worker-artifact-v1';
const TEMPORARY_METADATA_SCHEMA =
'qinglong-remote-worker-artifact-temporary-v1';
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
const BUCKET_PATTERN = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
const PREFIX_PATTERN = /^[A-Za-z0-9][A-Za-z0-9/_=-]{0,254}$/;
const TEMPORARY_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
type S3SendClient = Pick<S3Client, 'send'>;
export type S3ClusterRemoteWorkerArtifactEncryption =
| Readonly<{ readonly mode: 's3' }>
| Readonly<{ readonly mode: 'kms'; readonly keyId: string }>;
export interface S3ClusterRemoteWorkerArtifactStoreDiagnostic {
readonly operation: 'temporary_object_cleanup';
}
export interface S3ClusterRemoteWorkerArtifactStoreOptions {
readonly client: S3SendClient;
readonly bucket: string;
readonly prefix?: string;
readonly expectedBucketOwner?: string;
readonly encryption: S3ClusterRemoteWorkerArtifactEncryption;
readonly createTemporaryId?: () => string;
readonly onDiagnostic?: (
error: unknown,
context: Readonly<S3ClusterRemoteWorkerArtifactStoreDiagnostic>,
) => void | Promise<void>;
}
export interface S3ClusterRemoteWorkerArtifactClientOptions {
readonly region: string;
readonly endpoint?: string;
readonly forcePathStyle?: boolean;
}
export function createS3ClusterRemoteWorkerArtifactClient(
options: S3ClusterRemoteWorkerArtifactClientOptions,
): S3Client {
if (
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
!/^[a-z0-9][a-z0-9-]{0,62}$/.test(options.region) ||
(options.endpoint !== undefined &&
typeof options.endpoint !== 'string') ||
(options.forcePathStyle !== undefined &&
typeof options.forcePathStyle !== 'boolean')
) {
throw configurationError('client options are invalid');
}
return new S3Client({
region: options.region,
...(options.endpoint === undefined
? {}
: { endpoint: options.endpoint }),
forcePathStyle: options.forcePathStyle ?? false,
});
}
export class S3ClusterRemoteWorkerArtifactStoreError extends Error {
constructor(
readonly reason: 'unavailable' | 'integrity_mismatch',
options?: ErrorOptions,
) {
super(`S3 Remote Worker Artifact store failed: ${reason}`, options);
this.name = 'S3ClusterRemoteWorkerArtifactStoreError';
}
}
interface PreparedOptions {
readonly client: S3SendClient;
readonly bucket: string;
readonly prefix: string;
readonly expectedBucketOwner?: string;
readonly encryption: Readonly<{
readonly ServerSideEncryption: 'AES256' | 'aws:kms';
readonly SSEKMSKeyId?: string;
}>;
readonly createTemporaryId: () => string;
readonly onDiagnostic?: S3ClusterRemoteWorkerArtifactStoreOptions['onDiagnostic'];
}
type ArtifactAuthority = Readonly<{
projectId: string;
runId: string;
attemptId: string;
logArtifactId: string;
}>;
type NormalizedStorageCommand = ArtifactAuthority &
Readonly<{
byteLength: number;
truncated?: boolean;
}>;
const DIAGNOSTIC_CONTEXT = Object.freeze({
operation: 'temporary_object_cleanup' as const,
});
function configurationError(message: string): TypeError {
return new TypeError(`S3 Remote Worker Artifact store is invalid: ${message}`);
}
function prepareOptions(
options: S3ClusterRemoteWorkerArtifactStoreOptions,
): PreparedOptions {
const allowedKeys = new Set([
'bucket',
'client',
'createTemporaryId',
'encryption',
'expectedBucketOwner',
'onDiagnostic',
'prefix',
]);
if (
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
Object.keys(options).some((key) => !allowedKeys.has(key)) ||
typeof options.client?.send !== 'function'
) {
throw configurationError('options are invalid');
}
if (
!BUCKET_PATTERN.test(options.bucket) ||
options.bucket.includes('..') ||
/^\d{1,3}(?:\.\d{1,3}){3}$/.test(options.bucket)
) {
throw configurationError('bucket is invalid');
}
const prefix = options.prefix ?? DEFAULT_PREFIX;
if (
!PREFIX_PATTERN.test(prefix) ||
prefix.startsWith('/') ||
prefix.endsWith('/') ||
prefix.includes('//') ||
prefix.split('/').some((segment) => segment === '.' || segment === '..')
) {
throw configurationError('prefix is invalid');
}
if (
options.expectedBucketOwner !== undefined &&
!/^\d{12}$/.test(options.expectedBucketOwner)
) {
throw configurationError('expected bucket owner is invalid');
}
const encryption = options.encryption;
if (!encryption || typeof encryption !== 'object' || Array.isArray(encryption)) {
throw configurationError('encryption is required');
}
let preparedEncryption: PreparedOptions['encryption'];
if (
encryption.mode === 's3' &&
Object.keys(encryption).length === 1
) {
preparedEncryption = Object.freeze({
ServerSideEncryption: ServerSideEncryption.AES256,
});
} else if (
encryption.mode === 'kms' &&
Object.keys(encryption).length === 2 &&
typeof encryption.keyId === 'string' &&
encryption.keyId.length >= 1 &&
encryption.keyId.length <= 2048 &&
!/[\u0000-\u001f\u007f]/.test(encryption.keyId)
) {
preparedEncryption = Object.freeze({
ServerSideEncryption: ServerSideEncryption.aws_kms,
SSEKMSKeyId: encryption.keyId,
});
} else {
throw configurationError('encryption is invalid');
}
if (
options.createTemporaryId !== undefined &&
typeof options.createTemporaryId !== 'function'
) {
throw configurationError('temporary ID factory is invalid');
}
if (
options.onDiagnostic !== undefined &&
typeof options.onDiagnostic !== 'function'
) {
throw configurationError('diagnostic sink is invalid');
}
return Object.freeze({
client: options.client,
bucket: options.bucket,
prefix,
...(options.expectedBucketOwner === undefined
? {}
: { expectedBucketOwner: options.expectedBucketOwner }),
encryption: preparedEncryption,
createTemporaryId: options.createTemporaryId ?? randomUUID,
...(options.onDiagnostic === undefined
? {}
: { onDiagnostic: options.onDiagnostic }),
});
}
function receiptCandidate(
authority: ArtifactAuthority,
byteLength: number,
sha256: string,
truncated?: boolean,
status: RemoteWorkerArtifactReceipt['status'] = 'stored',
): Readonly<RemoteWorkerArtifactReceipt> {
return normalizeRemoteWorkerArtifactReceipt({
status,
...authority,
byteLength,
sha256,
...(truncated === undefined ? {} : { truncated }),
});
}
function exactObjectShape(
value: unknown,
required: readonly string[],
optional: readonly string[],
name: string,
): asserts value is Readonly<Record<string, unknown>> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw configurationError(`${name} is invalid`);
}
const keys = Object.keys(value);
const allowed = new Set([...required, ...optional]);
if (
required.some((key) => !Object.hasOwn(value, key)) ||
keys.some((key) => !allowed.has(key))
) {
throw configurationError(`${name} shape is invalid`);
}
}
function normalizeStorageCommand(
command: Readonly<ClusterRemoteWorkerArtifactStorageCommand>,
): NormalizedStorageCommand {
exactObjectShape(
command,
['attemptId', 'byteLength', 'logArtifactId', 'projectId', 'runId'],
['truncated'],
'storage command',
);
const normalized = receiptCandidate(
command as unknown as ClusterRemoteWorkerArtifactStorageCommand,
command.byteLength as number,
'0'.repeat(64),
command.truncated as boolean | undefined,
);
return Object.freeze({
projectId: normalized.projectId,
runId: normalized.runId,
attemptId: normalized.attemptId,
logArtifactId: normalized.logArtifactId,
byteLength: normalized.byteLength,
...(normalized.truncated === undefined
? {}
: { truncated: normalized.truncated }),
});
}
function normalizeLookup(
lookup: Readonly<ClusterRemoteWorkerArtifactLookup>,
): ArtifactAuthority {
exactObjectShape(
lookup,
['attemptId', 'logArtifactId', 'projectId', 'runId'],
[],
'lookup',
);
const normalized = receiptCandidate(
lookup as unknown as ClusterRemoteWorkerArtifactLookup,
0,
'0'.repeat(64),
);
return Object.freeze({
projectId: normalized.projectId,
runId: normalized.runId,
attemptId: normalized.attemptId,
logArtifactId: normalized.logArtifactId,
});
}
function lookupFromCommand(
command: NormalizedStorageCommand,
): ArtifactAuthority {
return Object.freeze({
projectId: command.projectId,
runId: command.runId,
attemptId: command.attemptId,
logArtifactId: command.logArtifactId,
});
}
function identityDigest(authority: ArtifactAuthority): string {
return createHash('sha256')
.update('qinglong/remote-worker-artifact-identity@v1\0', 'utf8')
.update(authority.projectId, 'utf8')
.update('\0', 'utf8')
.update(authority.runId, 'utf8')
.update('\0', 'utf8')
.update(authority.attemptId, 'utf8')
.update('\0', 'utf8')
.update(authority.logArtifactId, 'utf8')
.digest('hex');
}
function fieldDigest(domain: string, value: string): string {
return createHash('sha256')
.update(`qinglong/remote-worker-artifact-${domain}@v1\0`, 'utf8')
.update(value, 'utf8')
.digest('hex');
}
function finalObjectKey(prefix: string, authority: ArtifactAuthority): string {
const digest = identityDigest(authority);
return `${prefix}/objects/${digest.slice(0, 2)}/${digest}`;
}
function temporaryObjectKey(prefix: string, createId: () => string): string {
const id = createId();
if (typeof id !== 'string' || !TEMPORARY_ID_PATTERN.test(id)) {
throw new S3ClusterRemoteWorkerArtifactStoreError('unavailable');
}
return `${prefix}/temporary/${id}`;
}
function temporaryOwnershipDigest(): string {
const authority = randomBytes(32);
try {
return createHash('sha256')
.update('qinglong/remote-worker-artifact-temporary-owner@v1\0', 'utf8')
.update(authority)
.digest('hex');
} finally {
authority.fill(0);
}
}
function finalMetadata(
command: NormalizedStorageCommand,
sha256: string,
): Readonly<Record<string, string>> {
return Object.freeze({
'ql3-schema': METADATA_SCHEMA,
'ql3-project-sha256': fieldDigest('project', command.projectId),
'ql3-run-sha256': fieldDigest('run', command.runId),
'ql3-attempt-sha256': fieldDigest('attempt', command.attemptId),
'ql3-log-artifact-sha256': fieldDigest(
'log-artifact',
command.logArtifactId,
),
'ql3-byte-length': String(command.byteLength),
'ql3-content-sha256': sha256,
'ql3-truncated': command.truncated === undefined
? 'omitted'
: String(command.truncated),
});
}
function canonicalChecksum(value: unknown): string {
if (typeof value !== 'string' || value.length !== 44) {
throw new S3ClusterRemoteWorkerArtifactStoreError('integrity_mismatch');
}
const decoded = Buffer.from(value, 'base64');
try {
if (decoded.byteLength !== 32 || decoded.toString('base64') !== value) {
throw new S3ClusterRemoteWorkerArtifactStoreError('integrity_mismatch');
}
return decoded.toString('hex');
} finally {
decoded.fill(0);
}
}
function metadataValue(
metadata: Readonly<Record<string, string | undefined>> | undefined,
name: string,
): string {
const value = metadata?.[name];
if (typeof value !== 'string') {
throw new S3ClusterRemoteWorkerArtifactStoreError('integrity_mismatch');
}
return value;
}
function parseStoredReceipt(
authority: ArtifactAuthority,
output: Readonly<{
ContentLength?: number | undefined;
ContentType?: string | undefined;
ChecksumSHA256?: string | undefined;
Metadata?: Readonly<Record<string, string | undefined>> | undefined;
}>,
): Readonly<RemoteWorkerArtifactReceipt> {
const metadata = output.Metadata;
const lengthText = metadataValue(metadata, 'ql3-byte-length');
const byteLength = Number(lengthText);
const sha256 = metadataValue(metadata, 'ql3-content-sha256');
const truncatedText = metadataValue(metadata, 'ql3-truncated');
if (
metadataValue(metadata, 'ql3-schema') !== METADATA_SCHEMA ||
metadataValue(metadata, 'ql3-project-sha256') !==
fieldDigest('project', authority.projectId) ||
metadataValue(metadata, 'ql3-run-sha256') !==
fieldDigest('run', authority.runId) ||
metadataValue(metadata, 'ql3-attempt-sha256') !==
fieldDigest('attempt', authority.attemptId) ||
metadataValue(metadata, 'ql3-log-artifact-sha256') !==
fieldDigest('log-artifact', authority.logArtifactId) ||
!Number.isSafeInteger(byteLength) ||
byteLength < 0 ||
byteLength > MAX_REMOTE_WORKER_ARTIFACT_BYTES ||
lengthText !== String(byteLength) ||
output.ContentLength !== byteLength ||
output.ContentType !== REMOTE_WORKER_ARTIFACT_CONTENT_TYPE ||
!SHA256_PATTERN.test(sha256) ||
canonicalChecksum(output.ChecksumSHA256) !== sha256 ||
!['omitted', 'true', 'false'].includes(truncatedText)
) {
throw new S3ClusterRemoteWorkerArtifactStoreError('integrity_mismatch');
}
return receiptCandidate(
authority,
byteLength,
sha256,
truncatedText === 'omitted' ? undefined : truncatedText === 'true',
'already_stored',
);
}
function isNotFound(error: unknown): boolean {
if (!error || typeof error !== 'object') return false;
const value = error as {
name?: unknown;
Code?: unknown;
$metadata?: { httpStatusCode?: unknown };
};
return value.name === 'NotFound' ||
value.name === 'NoSuchKey' ||
value.Code === 'NoSuchKey' ||
value.$metadata?.httpStatusCode === 404;
}
function requestOptions(signal?: AbortSignal): { abortSignal: AbortSignal } | undefined {
return signal === undefined ? undefined : { abortSignal: signal };
}
function copySource(bucket: string, key: string): string {
return [bucket, ...key.split('/')]
.map((segment) => encodeURIComponent(segment))
.join('/');
}
class ArtifactContentDigest {
private readonly hash = createHash('sha256');
private consumedBytes = 0;
private complete = false;
private digestValue?: string;
constructor(
private readonly content: AsyncIterable<Uint8Array>,
private readonly expectedBytes: number,
private readonly signal?: AbortSignal,
) {
if (!content || typeof content[Symbol.asyncIterator] !== 'function') {
throw new S3ClusterRemoteWorkerArtifactStoreError('unavailable');
}
}
async *stream(): AsyncGenerator<Buffer> {
if (this.complete || this.consumedBytes !== 0) {
throw new S3ClusterRemoteWorkerArtifactStoreError('unavailable');
}
if (this.signal?.aborted) throw this.signal.reason;
for await (const chunk of this.content) {
if (this.signal?.aborted) throw this.signal.reason;
if (!(chunk instanceof Uint8Array) || chunk.byteLength === 0) {
throw new S3ClusterRemoteWorkerArtifactStoreError('integrity_mismatch');
}
this.consumedBytes += chunk.byteLength;
if (this.consumedBytes > this.expectedBytes) {
throw new S3ClusterRemoteWorkerArtifactStoreError('integrity_mismatch');
}
this.hash.update(chunk);
yield Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
}
if (this.consumedBytes !== this.expectedBytes) {
throw new S3ClusterRemoteWorkerArtifactStoreError('integrity_mismatch');
}
if (this.signal?.aborted) throw this.signal.reason;
this.digestValue = this.hash.digest('hex');
this.complete = true;
}
async consume(): Promise<string> {
for await (const _chunk of this.stream()) {
// Hash a replay without allocating or contacting object storage.
}
return this.digest();
}
digest(): string {
if (!this.complete || !this.digestValue) {
throw new S3ClusterRemoteWorkerArtifactStoreError('unavailable');
}
return this.digestValue;
}
isComplete(): boolean {
return this.complete;
}
}
/**
* Shared immutable S3 adapter. A unique temporary upload is checksummed first,
* then promoted by one destination-conditional server-side copy. Permanent
* objects are never overwritten or deleted by this adapter.
*/
export class S3ClusterRemoteWorkerArtifactStore
implements ClusterRemoteWorkerArtifactStore {
private readonly options: PreparedOptions;
constructor(options: S3ClusterRemoteWorkerArtifactStoreOptions) {
this.options = prepareOptions(options);
}
async inspect(
lookup: Readonly<ClusterRemoteWorkerArtifactLookup>,
signal?: AbortSignal,
): Promise<Readonly<RemoteWorkerArtifactReceipt> | undefined> {
const authority = normalizeLookup(lookup);
if (signal?.aborted) throw signal.reason;
try {
const output = await this.options.client.send(
new HeadObjectCommand({
Bucket: this.options.bucket,
Key: finalObjectKey(this.options.prefix, authority),
ChecksumMode: ChecksumMode.ENABLED,
...(this.options.expectedBucketOwner === undefined
? {}
: { ExpectedBucketOwner: this.options.expectedBucketOwner }),
}),
requestOptions(signal),
);
return parseStoredReceipt(authority, output);
} catch (error) {
if (isNotFound(error)) return undefined;
if (error instanceof S3ClusterRemoteWorkerArtifactStoreError) throw error;
throw new S3ClusterRemoteWorkerArtifactStoreError('unavailable', {
cause: error,
});
}
}
async put(
value: Readonly<ClusterRemoteWorkerArtifactStorageCommand>,
content: AsyncIterable<Uint8Array>,
signal?: AbortSignal,
): Promise<Readonly<RemoteWorkerArtifactReceipt>> {
const command = normalizeStorageCommand(value);
const digest = new ArtifactContentDigest(
content,
command.byteLength,
signal,
);
const lookup = lookupFromCommand(command);
const existing = await this.inspect(lookup, signal);
if (existing) {
const incomingSha256 = await digest.consume();
if (
existing.byteLength !== command.byteLength ||
existing.truncated !== command.truncated ||
existing.sha256 !== incomingSha256
) {
throw new S3ClusterRemoteWorkerArtifactStoreError(
'integrity_mismatch',
);
}
return existing;
}
const temporaryKey = temporaryObjectKey(
this.options.prefix,
this.options.createTemporaryId,
);
const temporaryOwner = temporaryOwnershipDigest();
let temporaryOwned = false;
let result: Readonly<RemoteWorkerArtifactReceipt> | undefined;
let primaryError: unknown;
try {
const body = Readable.from(digest.stream(), { objectMode: false });
try {
await this.options.client.send(
new PutObjectCommand({
Bucket: this.options.bucket,
Key: temporaryKey,
Body: body,
ContentLength: command.byteLength,
ContentType: REMOTE_WORKER_ARTIFACT_CONTENT_TYPE,
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
IfNoneMatch: '*',
Metadata: {
'ql3-schema': TEMPORARY_METADATA_SCHEMA,
'ql3-owner-sha256': temporaryOwner,
},
...this.options.encryption,
...(this.options.expectedBucketOwner === undefined
? {}
: { ExpectedBucketOwner: this.options.expectedBucketOwner }),
}),
requestOptions(signal),
);
temporaryOwned = true;
} catch (error) {
if (!digest.isComplete()) throw error;
} finally {
body.destroy();
}
const sha256 = digest.digest();
await this.assertTemporaryObject(
temporaryKey,
temporaryOwner,
command.byteLength,
sha256,
signal,
);
temporaryOwned = true;
let copied = false;
try {
await this.options.client.send(
new CopyObjectCommand({
Bucket: this.options.bucket,
Key: finalObjectKey(this.options.prefix, command),
CopySource: copySource(this.options.bucket, temporaryKey),
ContentType: REMOTE_WORKER_ARTIFACT_CONTENT_TYPE,
MetadataDirective: MetadataDirective.REPLACE,
Metadata: finalMetadata(command, sha256),
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
IfNoneMatch: '*',
...this.options.encryption,
...(this.options.expectedBucketOwner === undefined
? {}
: {
ExpectedBucketOwner: this.options.expectedBucketOwner,
CopySourceExpectedBucketOwner:
this.options.expectedBucketOwner,
}),
}),
requestOptions(signal),
);
copied = true;
} catch {
// A 409/412 race or a lost successful response is resolved only by
// inspecting the immutable destination below.
}
const stored = await this.inspect(lookup, signal);
if (
!stored ||
stored.byteLength !== command.byteLength ||
stored.truncated !== command.truncated ||
stored.sha256 !== sha256
) {
throw new S3ClusterRemoteWorkerArtifactStoreError(
'integrity_mismatch',
);
}
result = Object.freeze({
...stored,
status: copied ? 'stored' as const : 'already_stored' as const,
});
} catch (error) {
primaryError = error;
}
if (temporaryOwned) {
try {
await this.options.client.send(
new DeleteObjectCommand({
Bucket: this.options.bucket,
Key: temporaryKey,
...(this.options.expectedBucketOwner === undefined
? {}
: { ExpectedBucketOwner: this.options.expectedBucketOwner }),
}),
requestOptions(signal),
);
} catch (error) {
if (primaryError === undefined) {
try {
await this.options.onDiagnostic?.(error, DIAGNOSTIC_CONTEXT);
} catch {
// Diagnostics cannot reverse a durable immutable promotion.
}
}
}
}
if (primaryError !== undefined) {
if (primaryError instanceof S3ClusterRemoteWorkerArtifactStoreError) {
throw primaryError;
}
throw new S3ClusterRemoteWorkerArtifactStoreError('unavailable', {
cause: primaryError,
});
}
return result!;
}
private async assertTemporaryObject(
key: string,
ownerSha256: string,
byteLength: number,
sha256: string,
signal?: AbortSignal,
): Promise<void> {
let output;
try {
output = await this.options.client.send(
new HeadObjectCommand({
Bucket: this.options.bucket,
Key: key,
ChecksumMode: ChecksumMode.ENABLED,
...(this.options.expectedBucketOwner === undefined
? {}
: { ExpectedBucketOwner: this.options.expectedBucketOwner }),
}),
requestOptions(signal),
);
} catch (error) {
throw new S3ClusterRemoteWorkerArtifactStoreError('unavailable', {
cause: error,
});
}
if (
output.ContentLength !== byteLength ||
output.ContentType !== REMOTE_WORKER_ARTIFACT_CONTENT_TYPE ||
output.Metadata?.['ql3-schema'] !== TEMPORARY_METADATA_SCHEMA ||
output.Metadata?.['ql3-owner-sha256'] !== ownerSha256 ||
canonicalChecksum(output.ChecksumSHA256) !== sha256
) {
throw new S3ClusterRemoteWorkerArtifactStoreError(
'integrity_mismatch',
);
}
}
}
@@ -0,0 +1,45 @@
// Artifact owns lazy production binding without widening the Worker runtime port.
import {
createS3ClusterRemoteWorkerArtifactClient,
S3ClusterRemoteWorkerArtifactStore,
} from './s3ArtifactStore';
import type { ClusterRemoteWorkerArtifactStore } from '../remote-execution/remoteWorkerCompletionService';
import type { ClusterWorkerArtifactS3Config } from '../worker-ingress/workerIngressConfig';
export interface ClusterWorkerArtifactBinding {
readonly store: ClusterRemoteWorkerArtifactStore;
close(): Promise<void>;
}
export function createClusterWorkerArtifactBinding(
config: ClusterWorkerArtifactS3Config,
): Readonly<ClusterWorkerArtifactBinding> {
if (!config || typeof config !== 'object' || Array.isArray(config)) {
throw new TypeError('Cluster Worker Artifact binding config is invalid');
}
const client = createS3ClusterRemoteWorkerArtifactClient({
region: config.region,
...(config.endpoint === undefined
? {}
: { endpoint: config.endpoint }),
forcePathStyle: config.forcePathStyle,
});
const store = new S3ClusterRemoteWorkerArtifactStore({
client,
bucket: config.bucket,
...(config.prefix === undefined ? {} : { prefix: config.prefix }),
...(config.expectedBucketOwner === undefined
? {}
: { expectedBucketOwner: config.expectedBucketOwner }),
encryption: config.encryption,
});
let closed = false;
return Object.freeze({
store,
async close() {
if (closed) return;
closed = true;
client.destroy();
},
});
}
@@ -0,0 +1,253 @@
// Authentication owns credential verification and bounded Principal issuance.
import { createHmac, timingSafeEqual } from 'node:crypto';
import {
ApiCredentialUnavailableError,
LEGACY_API_CREDENTIAL_PEPPER_KEY_ID,
assertApiCredentialPepperKeyId,
normalizeApiCredentialRecord,
type ApiCredentialRepository,
} from '@qinglong/runtime-core/api-credential';
import {
normalizeSecurityPrincipal,
type SecurityPrincipal,
} from '@qinglong/runtime-core/security';
import type { ClusterControlAdmissionMetadata } from '../transport/httpSurface';
import type { ClusterControlRequestAuthenticator } from '../transport/admissionPipeline';
export const CLUSTER_CONTROL_API_CREDENTIAL_LIMITS = Object.freeze({
principalTtlMs: 60_000,
maxPrincipalTtlMs: 300_000,
secretBytes: 32,
});
export class ClusterControlApiCredentialConfigurationError extends TypeError {
constructor(message: string) {
super(
`Cluster-control API credential configuration is invalid: ${message}`,
);
this.name = 'ClusterControlApiCredentialConfigurationError';
}
}
export class ClusterControlApiCredentialUnavailableError extends Error {
readonly code = 'CLUSTER_CONTROL_API_CREDENTIAL_UNAVAILABLE';
constructor() {
super('Cluster-control API credential authentication is unavailable');
this.name = 'ClusterControlApiCredentialUnavailableError';
}
}
export interface ClusterControlApiCredentialAuthenticatorOptions {
readonly principalTtlMs?: number;
readonly pepperKeyId?: string;
readonly now?: () => number;
}
const AUTHORIZATION_PATTERN =
/^Bearer ql3c_([A-Za-z0-9][A-Za-z0-9._:-]{0,63})_([A-Za-z0-9_-]{43})$/;
const PEPPER_PATTERN = /^[A-Za-z0-9_-]{43}$/;
const DIGEST_DOMAIN = Buffer.from('qinglong-api-credential-v1\0', 'utf8');
function decodeSecret(name: string, value: string): Buffer {
if (typeof value !== 'string' || !PEPPER_PATTERN.test(value)) {
throw new ClusterControlApiCredentialConfigurationError(
`${name} must be canonical base64url for 32 bytes`,
);
}
const decoded = Buffer.from(value, 'base64url');
if (
decoded.byteLength !== CLUSTER_CONTROL_API_CREDENTIAL_LIMITS.secretBytes ||
decoded.toString('base64url') !== value
) {
throw new ClusterControlApiCredentialConfigurationError(
`${name} must be canonical base64url for 32 bytes`,
);
}
return decoded;
}
export function assertClusterControlApiCredentialPepper(value: string): void {
const decoded = decodeSecret('pepper', value);
decoded.fill(0);
}
function principalTtl(value: number | undefined): number {
const resolved =
value ?? CLUSTER_CONTROL_API_CREDENTIAL_LIMITS.principalTtlMs;
if (
!Number.isSafeInteger(resolved) ||
resolved < 1_000 ||
resolved > CLUSTER_CONTROL_API_CREDENTIAL_LIMITS.maxPrincipalTtlMs
) {
throw new ClusterControlApiCredentialConfigurationError(
'principalTtlMs is invalid',
);
}
return resolved;
}
function digest(pepper: Buffer, credentialId: string, secret: Buffer): Buffer {
return createHmac('sha256', pepper)
.update(DIGEST_DOMAIN)
.update(credentialId, 'utf8')
.update('\0', 'utf8')
.update(secret)
.digest();
}
export function apiCredentialSecretDigest(
pepperBase64Url: string,
credentialId: string,
secretBase64Url: string,
): string {
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/.test(credentialId)) {
throw new ClusterControlApiCredentialConfigurationError(
'credentialId is invalid',
);
}
const pepper = decodeSecret('pepper', pepperBase64Url);
const secret = decodeSecret('secret', secretBase64Url);
let result: Buffer | undefined;
try {
result = digest(pepper, credentialId, secret);
return result.toString('hex');
} finally {
result?.fill(0);
pepper.fill(0);
secret.fill(0);
}
}
function parseAuthorization(
metadata: ClusterControlAdmissionMetadata,
): { readonly credentialId: string; readonly secret: Buffer } | null {
const value = metadata.headers.authorization;
if (typeof value !== 'string') return null;
const match = AUTHORIZATION_PATTERN.exec(value);
if (!match) return null;
let secret: Buffer;
try {
secret = decodeSecret('bearer secret', match[2]!);
} catch {
return null;
}
return Object.freeze({ credentialId: match[1]!, secret });
}
export function createClusterControlApiCredentialAuthenticator(
repository: ApiCredentialRepository,
pepperBase64Url: string,
options: ClusterControlApiCredentialAuthenticatorOptions = {},
): ClusterControlRequestAuthenticator {
if (!repository || typeof repository.resolve !== 'function') {
throw new ClusterControlApiCredentialConfigurationError(
'repository is invalid',
);
}
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new ClusterControlApiCredentialConfigurationError(
'options are invalid',
);
}
const keys = Object.keys(options);
if (
keys.some(
(key) =>
key !== 'principalTtlMs' && key !== 'pepperKeyId' && key !== 'now',
)
) {
throw new ClusterControlApiCredentialConfigurationError(
'options shape is invalid',
);
}
if (options.now !== undefined && typeof options.now !== 'function') {
throw new ClusterControlApiCredentialConfigurationError('now is invalid');
}
const pepperKeyId =
options.pepperKeyId ?? LEGACY_API_CREDENTIAL_PEPPER_KEY_ID;
try {
assertApiCredentialPepperKeyId(pepperKeyId);
} catch {
throw new ClusterControlApiCredentialConfigurationError(
'pepperKeyId is invalid',
);
}
const pepper = decodeSecret('pepper', pepperBase64Url);
const ttlMs = principalTtl(options.principalTtlMs);
const now = options.now ?? Date.now;
return Object.freeze({
async authenticate(
metadata: ClusterControlAdmissionMetadata,
): Promise<Readonly<SecurityPrincipal> | null> {
const parsed = parseAuthorization(metadata);
if (!parsed) return null;
const presentedDigest = digest(
pepper,
parsed.credentialId,
parsed.secret,
);
parsed.secret.fill(0);
let candidate;
try {
candidate = await repository.resolve(parsed.credentialId);
} catch (error) {
presentedDigest.fill(0);
if (error instanceof ApiCredentialUnavailableError) {
throw new ClusterControlApiCredentialUnavailableError();
}
throw new ClusterControlApiCredentialUnavailableError();
}
if (metadata.signal.aborted) {
presentedDigest.fill(0);
throw new ClusterControlApiCredentialUnavailableError();
}
let record;
try {
record = candidate ? normalizeApiCredentialRecord(candidate) : null;
} catch {
presentedDigest.fill(0);
throw new ClusterControlApiCredentialUnavailableError();
}
if (record && record.pepperKeyId !== pepperKeyId) {
presentedDigest.fill(0);
throw new ClusterControlApiCredentialUnavailableError();
}
const storedDigest = record
? Buffer.from(record.secretDigest, 'hex')
: Buffer.alloc(32);
const matches = timingSafeEqual(presentedDigest, storedDigest);
presentedDigest.fill(0);
storedDigest.fill(0);
if (!record || !matches) return null;
const nowMs = now();
if (
!Number.isSafeInteger(nowMs) ||
nowMs < 0 ||
record.state !== 'active' ||
record.subjectStatus !== 'active' ||
record.notBeforeAtMs > nowMs ||
record.expiresAtMs <= nowMs
) {
return null;
}
const expiresAtMs = Math.min(record.expiresAtMs, nowMs + ttlMs);
try {
return normalizeSecurityPrincipal(
{
subject: record.subject,
authenticationId: `api_credential:${record.credentialId}:${record.version}`,
authenticatedAtMs: nowMs,
expiresAtMs,
assurance:
record.subject.type === 'user' ? 'single_factor' : 'service',
},
nowMs,
);
} catch {
throw new ClusterControlApiCredentialUnavailableError();
}
},
});
}
@@ -0,0 +1,244 @@
// Authentication owns its bounded pre-body overload shield.
import { createHmac, randomBytes } from 'node:crypto';
import { performance } from 'node:perf_hooks';
export interface ClusterControlAuthenticationShieldOptions {
readonly windowMs: number;
readonly maxRequestsPerPeer: number;
readonly maxRequestsGlobal: number;
readonly maxTrackedPeers: number;
readonly now?: () => number;
}
export type ClusterControlAuthenticationShieldRejectionReason =
| 'capacity'
| 'clock'
| 'global'
| 'peer';
export type ClusterControlAuthenticationShieldResult =
| {
readonly allowed: true;
/**
* Returns this provisional attempt budget after the pre-body admission
* preflight has succeeded. Idempotent and scoped to the exact windows
* consumed by this result.
*/
refund(): void;
}
| {
readonly allowed: false;
readonly reason: ClusterControlAuthenticationShieldRejectionReason;
readonly retryAfterMs: number;
};
export interface ClusterControlAuthenticationShield {
consume(
peerAddress: string | undefined,
): ClusterControlAuthenticationShieldResult;
close(): void;
}
interface PeerWindow {
readonly startedAt: number;
readonly count: number;
}
const FINGERPRINT_KEY_BYTES = 32;
const MAX_PEER_ADDRESS_BYTES = 128;
const MAX_PRUNE_PER_ATTEMPT = 64;
const UNKNOWN_PEER = '<unknown-transport-peer>';
function normalizedPeerAddress(peerAddress: string | undefined): string {
if (
typeof peerAddress !== 'string' ||
peerAddress.length === 0 ||
Buffer.byteLength(peerAddress) > MAX_PEER_ADDRESS_BYTES ||
/[\0\r\n]/.test(peerAddress)
) {
return UNKNOWN_PEER;
}
return peerAddress;
}
function remainingWindow(
now: number,
startedAt: number,
windowMs: number,
): number {
return Math.max(1, Math.ceil(windowMs - (now - startedAt)));
}
/**
* Creates a process-local overload shield for authentication attempts. It is
* deliberately not an authorization or distributed quota authority: every
* cluster-control replica owns a bounded, disposable window.
*/
export function createClusterControlAuthenticationShield(
options: ClusterControlAuthenticationShieldOptions,
): ClusterControlAuthenticationShield {
const now = options.now ?? (() => performance.now());
const fingerprintKey = randomBytes(FINGERPRINT_KEY_BYTES);
const peers = new Map<string, PeerWindow>();
let globalWindow: PeerWindow | undefined;
let lastNow = 0;
let closed = false;
const fingerprint = (peerAddress: string | undefined): string =>
createHmac('sha256', fingerprintKey)
.update('qinglong.cluster-control.authentication-peer\0')
.update(normalizedPeerAddress(peerAddress))
.digest('base64url');
const pruneExpired = (currentTime: number): void => {
let scanned = 0;
for (const [key, window] of peers) {
if (scanned >= MAX_PRUNE_PER_ATTEMPT) return;
scanned += 1;
if (currentTime - window.startedAt >= options.windowMs) {
peers.delete(key);
}
}
};
const accepted = (
peer: string,
peerStartedAt: number,
globalStartedAt: number,
): ClusterControlAuthenticationShieldResult => {
let completed = false;
return Object.freeze({
allowed: true as const,
refund() {
if (completed || closed) return;
completed = true;
if (
globalWindow?.startedAt === globalStartedAt &&
globalWindow.count > 0
) {
globalWindow = {
startedAt: globalWindow.startedAt,
count: globalWindow.count - 1,
};
}
const currentPeerWindow = peers.get(peer);
if (
currentPeerWindow?.startedAt === peerStartedAt &&
currentPeerWindow.count > 0
) {
if (currentPeerWindow.count === 1) peers.delete(peer);
else {
peers.set(peer, {
startedAt: currentPeerWindow.startedAt,
count: currentPeerWindow.count - 1,
});
}
}
},
});
};
return {
consume(peerAddress) {
if (closed) {
return Object.freeze({
allowed: false,
reason: 'clock',
retryAfterMs: options.windowMs,
});
}
let currentTime: number;
try {
currentTime = now();
} catch {
return Object.freeze({
allowed: false,
reason: 'clock',
retryAfterMs: options.windowMs,
});
}
if (
!Number.isFinite(currentTime) ||
currentTime < 0 ||
currentTime < lastNow
) {
return Object.freeze({
allowed: false,
reason: 'clock',
retryAfterMs: options.windowMs,
});
}
lastNow = currentTime;
if (
!globalWindow ||
currentTime - globalWindow.startedAt >= options.windowMs
) {
globalWindow = { startedAt: currentTime, count: 0 };
}
if (globalWindow.count >= options.maxRequestsGlobal) {
return Object.freeze({
allowed: false,
reason: 'global',
retryAfterMs: remainingWindow(
currentTime,
globalWindow.startedAt,
options.windowMs,
),
});
}
globalWindow = {
startedAt: globalWindow.startedAt,
count: globalWindow.count + 1,
};
const peer = fingerprint(peerAddress);
let peerWindow = peers.get(peer);
if (
peerWindow &&
currentTime - peerWindow.startedAt >= options.windowMs
) {
peers.delete(peer);
peerWindow = undefined;
}
if (peerWindow) {
if (peerWindow.count >= options.maxRequestsPerPeer) {
return Object.freeze({
allowed: false,
reason: 'peer',
retryAfterMs: remainingWindow(
currentTime,
peerWindow.startedAt,
options.windowMs,
),
});
}
peers.delete(peer);
peers.set(peer, {
startedAt: peerWindow.startedAt,
count: peerWindow.count + 1,
});
return accepted(peer, peerWindow.startedAt, globalWindow.startedAt);
}
if (peers.size >= options.maxTrackedPeers) pruneExpired(currentTime);
if (peers.size >= options.maxTrackedPeers) {
return Object.freeze({
allowed: false,
reason: 'capacity',
retryAfterMs: options.windowMs,
});
}
peers.set(peer, { startedAt: currentTime, count: 1 });
return accepted(peer, currentTime, globalWindow.startedAt);
},
close() {
if (closed) return;
closed = true;
peers.clear();
globalWindow = undefined;
fingerprintKey.fill(0);
},
};
}
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env node
import {
runProductionClusterControlProcess,
type ClusterControlProcessEvent,
type ClusterControlProcessSignal,
type ClusterControlProcessSignalSource,
} from './production-process/processApplication';
const USAGE = 'Usage: ql3-cluster-control';
const nodeSignals: ClusterControlProcessSignalSource = Object.freeze({
subscribe(
listener: (signal: ClusterControlProcessSignal) => void,
) {
const handlers: Readonly<
Record<ClusterControlProcessSignal, () => void>
> = Object.freeze({
SIGINT: () => listener('SIGINT'),
SIGTERM: () => listener('SIGTERM'),
});
process.once('SIGINT', handlers.SIGINT);
process.once('SIGTERM', handlers.SIGTERM);
return () => {
process.off('SIGINT', handlers.SIGINT);
process.off('SIGTERM', handlers.SIGTERM);
};
},
});
function emit(record: ClusterControlProcessEvent): void {
process.stdout.write(`${JSON.stringify(record)}\n`);
}
function failureFact(error: unknown): Readonly<Record<string, unknown>> {
const candidate = error as {
readonly name?: unknown;
readonly code?: unknown;
};
return Object.freeze({
schemaVersion: 1,
component: 'qinglong3-cluster-control',
level: 'error',
event: 'process_failed',
name:
typeof candidate?.name === 'string' && candidate.name.length <= 128
? candidate.name
: 'Error',
...(typeof candidate?.code === 'string' && candidate.code.length <= 128
? { code: candidate.code }
: {}),
});
}
async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`);
return;
}
if (argv.length !== 0) {
process.stderr.write(
`${JSON.stringify({
code: 'QL3_CLUSTER_CONTROL_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const stopResult = await runProductionClusterControlProcess({
environment: process.env,
signals: nodeSignals,
emit,
});
if (stopResult !== 'stopped') process.exitCode = 1;
} catch (error) {
process.stderr.write(`${JSON.stringify(failureFact(error))}\n`);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,93 @@
// Database owns the one-way Pool failure to admission-withdrawal fence.
export type ClusterControlAvailabilityStatus =
| 'available'
| 'unavailable'
| 'disposed';
export type ClusterControlUnavailableListener = (
error: Error,
) => void | Promise<void>;
export interface ClusterControlAvailabilitySource {
subscribe(listener: ClusterControlUnavailableListener): () => void;
}
export type ClusterControlAvailabilitySignalResult =
| 'signaled'
| 'already_unavailable'
| 'disposed';
/**
* A bounded one-way bridge from pg.Pool availability errors to the application
* admission owner. It deliberately has one listener and no timer, retry loop,
* error history or path back to available.
*/
export class ClusterControlAvailabilityFence
implements ClusterControlAvailabilitySource
{
private currentStatus: ClusterControlAvailabilityStatus = 'available';
private listener: ClusterControlUnavailableListener | undefined;
private reason: Error | undefined;
private notification: Promise<void> | undefined;
get status(): ClusterControlAvailabilityStatus {
return this.currentStatus;
}
subscribe(listener: ClusterControlUnavailableListener): () => void {
if (typeof listener !== 'function') {
throw new TypeError('Cluster-control availability listener is invalid');
}
if (this.currentStatus === 'disposed') {
throw new Error('Cluster-control availability fence is disposed');
}
if (this.listener) {
throw new Error('Cluster-control availability listener is already bound');
}
this.listener = listener;
if (this.currentStatus === 'unavailable') {
// An early signal has already returned to its producer. The subscriber
// owns the delayed notification, so contain its rejection here.
void this.notify().catch(() => undefined);
}
let subscribed = true;
return () => {
if (!subscribed) return;
subscribed = false;
if (this.listener === listener) this.listener = undefined;
};
}
signal(error: Error): Promise<ClusterControlAvailabilitySignalResult> {
if (!(error instanceof Error)) {
return Promise.reject(
new TypeError('Cluster-control availability error is invalid'),
);
}
if (this.currentStatus === 'disposed') return Promise.resolve('disposed');
if (this.currentStatus === 'unavailable') {
return (this.notification ?? Promise.resolve()).then(
() => 'already_unavailable' as const,
);
}
this.currentStatus = 'unavailable';
this.reason = error;
return this.notify().then(() => 'signaled' as const);
}
dispose(): void {
if (this.currentStatus === 'disposed') return;
this.currentStatus = 'disposed';
this.listener = undefined;
this.reason = undefined;
}
private notify(): Promise<void> {
if (this.notification) return this.notification;
if (!this.listener || !this.reason) return Promise.resolve();
const listener = this.listener;
const reason = this.reason;
this.notification = Promise.resolve().then(() => listener(reason));
return this.notification;
}
}
@@ -0,0 +1,88 @@
import type { ClusterControlAdmissionResponse } from '../../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../../transport/routeRegistry';
export const CLUSTER_PLUGIN_PACKAGE_PROMPT_CATALOG_RESPONSE_SCHEMA =
'qinglong/plugin-package-prompt-catalog@v1' as const;
export interface ClusterPluginPackagePromptCatalogCapability {
inspect(
projectId: string,
packageName: string,
): Promise<
Readonly<{
schema: typeof CLUSTER_PLUGIN_PACKAGE_PROMPT_CATALOG_RESPONSE_SCHEMA;
projectId: string;
packageName: string;
found: boolean;
publicationState: 'active' | 'withdrawn' | 'absent' | null;
prompts: readonly Readonly<{
id: string;
name: string;
description: string | null;
parameters: readonly Readonly<{
name: string;
description: string | null;
required: boolean;
}>[];
}>[];
}>
>;
}
const PACKAGE_NAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
export function createClusterControlPluginPackagePromptCatalogRoute(
capability: ClusterPluginPackagePromptCatalogCapability,
): Readonly<ClusterControlRouteDefinition> {
if (!capability || typeof capability.inspect !== 'function') {
throw new TypeError('Cluster-control Prompt catalog capability is invalid');
}
return Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/prompts',
operationId: 'prompt.read',
permission: 'model.invoke',
projectParameter: 'projectId' as const,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (
authorized.request.body !== null ||
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName)
) {
return response(400, { code: 'invalid_prompt_catalog_request' });
}
try {
const result = await capability.inspect(
authorized.projectId,
parameters.packageName,
);
if (
result.schema !==
CLUSTER_PLUGIN_PACKAGE_PROMPT_CATALOG_RESPONSE_SCHEMA ||
result.projectId !== authorized.projectId ||
result.packageName !== parameters.packageName
) {
return response(503, { code: 'prompt_catalog_unavailable' });
}
return response(200, result);
} catch {
return response(503, { code: 'prompt_catalog_unavailable' });
}
},
});
}
@@ -0,0 +1,150 @@
import { randomUUID } from 'node:crypto';
import {
normalizeSecurityAuditRecord,
type SecurityAuditRecord,
} from '@qinglong/runtime-core/security-audit';
import type {
SecurityPolicyFence,
SecuritySubject,
} from '@qinglong/runtime-core/security';
import type { ClusterControlAdmissionResponse } from '../../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../../transport/routeRegistry';
export const CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_EXECUTION_INSPECTION_ROUTE =
Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/prompts/{promptId}/executions/{executionRequestId}',
operationId: 'prompt.execution.read',
permission: 'run.read',
projectParameter: 'projectId',
});
export interface ClusterPluginPackagePromptExecutionInspectionRouteOptions {
readonly now?: () => number;
readonly createEventId?: () => string;
}
export interface ClusterPluginPackagePromptExecutionInspectionCapability {
inspectAuthorized(input: Readonly<{
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
actor: Readonly<SecuritySubject>;
fence: Readonly<SecurityPolicyFence>;
audit: Readonly<SecurityAuditRecord>;
}>): Promise<Readonly<{ found: boolean }>>;
}
const PACKAGE_NAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
const PROMPT_ID = /^[a-z][a-z0-9-]{0,62}$/;
const IDENTITY = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
const UUID_V4 =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function errorCode(error: unknown): string | null {
return error &&
typeof error === 'object' &&
'code' in error &&
typeof error.code === 'string'
? error.code
: null;
}
/** Exact, content-free recovery read keyed by the caller-known requestId. */
export function createClusterControlPluginPackagePromptExecutionInspectionRoute(
capability: ClusterPluginPackagePromptExecutionInspectionCapability,
options: ClusterPluginPackagePromptExecutionInspectionRouteOptions = {},
): Readonly<ClusterControlRouteDefinition> {
if (
!capability ||
typeof capability.inspectAuthorized !== 'function' ||
(options.now !== undefined && typeof options.now !== 'function') ||
(options.createEventId !== undefined &&
typeof options.createEventId !== 'function')
) {
throw new TypeError(
'Cluster-control Prompt execution inspection capability is invalid',
);
}
const now = options.now ?? Date.now;
const createEventId = options.createEventId ?? randomUUID;
return Object.freeze({
...CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_EXECUTION_INSPECTION_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
const observedAtMs = now();
const auditEventId = createEventId();
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.promptId !== 'string' ||
!PROMPT_ID.test(parameters.promptId) ||
typeof parameters.executionRequestId !== 'string' ||
!IDENTITY.test(parameters.executionRequestId) ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null ||
!Number.isSafeInteger(observedAtMs) ||
observedAtMs < 0 ||
typeof auditEventId !== 'string' ||
!UUID_V4.test(auditEventId)
) {
return response(503, { code: 'prompt_execution_inspection_unavailable' });
}
try {
const result = await capability.inspectAuthorized({
projectId: authorized.projectId,
packageName: parameters.packageName,
promptId: parameters.promptId,
executionRequestId: parameters.executionRequestId,
actor: authorized.principal.subject,
fence: {
projectVersion: authorized.policyFence.projectVersion,
bindingVersion: authorized.policyFence.bindingVersion,
},
audit: normalizeSecurityAuditRecord({
eventId: auditEventId,
requestId: authorized.request.requestId,
operationId: 'prompt.execution.read',
projectId: authorized.projectId,
subject: authorized.principal.subject,
authenticationId: authorized.principal.authenticationId,
outcome: 'allowed',
reasons: ['project_policy_allowed'],
fence: authorized.policyFence,
occurredAtMs: observedAtMs,
}),
});
return result.found
? response(200, { ...result })
: response(404, { code: 'prompt_execution_not_found' });
} catch (error) {
return errorCode(error) ===
'PLUGIN_PACKAGE_PROMPT_EXECUTION_INSPECTION_AUTHORIZATION_FENCE_CONFLICT'
? response(409, { code: 'authorization_fence_conflict' })
: response(503, {
code: 'prompt_execution_inspection_unavailable',
});
}
},
});
}
@@ -0,0 +1,277 @@
// Plugin Package Prompt owns request-keyed durable output recovery.
import type { SecurityPrincipal } from '@qinglong/runtime-core/security';
import type { ClusterControlAdmissionResponse } from '../../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../../transport/routeRegistry';
export const CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_OUTPUT_READ_RESPONSE_SCHEMA =
'qinglong/cluster-plugin-package-prompt-execution-output-read-response@v1' as const;
export const CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_EXECUTION_OUTPUT_READ_ROUTE =
Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/prompts/{promptId}/executions/{executionRequestId}/output',
operationId: 'prompt.execution.output.read',
permission: 'artifact.read',
projectParameter: 'projectId',
});
export interface ClusterPluginPackagePromptExecutionOutputReadCapability {
read(command: Readonly<{
principal: Readonly<SecurityPrincipal>;
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
}>): Promise<unknown>;
}
const IDENTITY = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$/;
const PACKAGE_NAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
const PROMPT_ID = /^[a-z][a-z0-9-]{0,62}$/;
const MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/;
const DIGEST = /^[0-9a-f]{64}$/;
const KEY_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
const FINISH_REASONS = new Set([
'stop',
'length',
'content_filter',
'tool_call',
'unknown',
]);
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function exactRecord(
value: unknown,
required: readonly string[],
optional: readonly string[] = [],
): Record<string, unknown> | null {
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
const record = value as Record<string, unknown>;
const keys = Object.keys(record).sort();
const expected = [
...required,
...optional.filter((key) => key in record),
].sort();
return keys.length === expected.length &&
keys.every((key, index) => key === expected[index])
? record
: null;
}
function nonNegativeInteger(value: unknown): value is number {
return Number.isSafeInteger(value) && (value as number) >= 0;
}
function exactTarget(
value: Record<string, unknown>,
expected: Readonly<{
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
}>,
): boolean {
return (
value.projectId === expected.projectId &&
value.packageName === expected.packageName &&
value.promptId === expected.promptId &&
value.executionRequestId === expected.executionRequestId
);
}
function availableView(
value: unknown,
expected: Readonly<{
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
}>,
): Readonly<Record<string, unknown>> | null {
const envelope = exactRecord(value, [
'executionRequestId',
'packageName',
'projectId',
'promptId',
'reference',
'result',
'schema',
'status',
]);
if (
!envelope ||
envelope.schema !==
'qinglong/plugin-package-prompt-execution-output-read-result@v1' ||
envelope.status !== 'available' ||
!exactTarget(envelope, expected)
) {
return null;
}
const reference = exactRecord(envelope.reference, [
'algorithm',
'artifactDigest',
'artifactId',
'contentDigest',
'invocationId',
'keyId',
'outputBytes',
'projectId',
'retentionEligibleAtMs',
'retentionPolicyDigest',
'runId',
'schema',
'stepRunId',
]);
const result = exactRecord(envelope.result, [
'finishReason',
'model',
'provider',
'text',
'usage',
]);
const usage = result
? exactRecord(
result.usage,
['inputTokens', 'outputTokens', 'totalTokens'],
['costMicros'],
)
: null;
if (
!reference ||
!result ||
!usage ||
reference.schema !==
'qinglong/plugin-package-prompt-output-artifact-reference@v1' ||
reference.algorithm !== 'aes-256-gcm' ||
reference.projectId !== expected.projectId ||
typeof reference.runId !== 'string' ||
!RUN_ID.test(reference.runId) ||
typeof reference.artifactId !== 'string' ||
!IDENTITY.test(reference.artifactId) ||
typeof reference.artifactDigest !== 'string' ||
!DIGEST.test(reference.artifactDigest) ||
typeof reference.stepRunId !== 'string' ||
!IDENTITY.test(reference.stepRunId) ||
typeof reference.invocationId !== 'string' ||
!IDENTITY.test(reference.invocationId) ||
typeof reference.contentDigest !== 'string' ||
!DIGEST.test(reference.contentDigest) ||
!nonNegativeInteger(reference.outputBytes) ||
reference.outputBytes > 1024 * 1024 ||
typeof reference.retentionPolicyDigest !== 'string' ||
!DIGEST.test(reference.retentionPolicyDigest) ||
!nonNegativeInteger(reference.retentionEligibleAtMs) ||
typeof reference.keyId !== 'string' ||
!KEY_ID.test(reference.keyId) ||
typeof result.provider !== 'string' ||
!MODEL_ID.test(result.provider) ||
typeof result.model !== 'string' ||
!MODEL_ID.test(result.model) ||
typeof result.text !== 'string' ||
Buffer.byteLength(result.text, 'utf8') > 1024 * 1024 ||
!FINISH_REASONS.has(result.finishReason as string) ||
!nonNegativeInteger(usage.inputTokens) ||
!nonNegativeInteger(usage.outputTokens) ||
!nonNegativeInteger(usage.totalTokens) ||
usage.totalTokens !== usage.inputTokens + usage.outputTokens ||
(usage.costMicros !== undefined && !nonNegativeInteger(usage.costMicros))
) {
return null;
}
return Object.freeze({
schema:
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_OUTPUT_READ_RESPONSE_SCHEMA,
status: 'available',
...expected,
reference: Object.freeze({ ...reference }),
result: Object.freeze({
provider: result.provider,
model: result.model,
text: result.text,
finishReason: result.finishReason,
usage: Object.freeze({ ...usage }),
}),
});
}
export function createClusterControlPluginPackagePromptExecutionOutputReadRoute(
capability: ClusterPluginPackagePromptExecutionOutputReadCapability,
): Readonly<ClusterControlRouteDefinition> {
if (!capability || typeof capability.read !== 'function') {
throw new TypeError(
'Cluster-control Prompt execution output read capability is invalid',
);
}
return Object.freeze({
...CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_EXECUTION_OUTPUT_READ_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (
authorized.request.body !== null ||
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.promptId !== 'string' ||
!PROMPT_ID.test(parameters.promptId) ||
typeof parameters.executionRequestId !== 'string' ||
!IDENTITY.test(parameters.executionRequestId)
) {
return response(400, {
code: 'invalid_prompt_execution_output_read_request',
});
}
const expected = Object.freeze({
projectId: authorized.projectId,
packageName: parameters.packageName,
promptId: parameters.promptId,
executionRequestId: parameters.executionRequestId,
});
try {
const result = await capability.read({
principal: authorized.principal,
...expected,
});
const notFound = exactRecord(result, [
'executionRequestId',
'packageName',
'projectId',
'promptId',
'schema',
'status',
]);
if (
notFound &&
notFound.schema ===
'qinglong/plugin-package-prompt-execution-output-read-result@v1' &&
notFound.status === 'not_found' &&
exactTarget(notFound, expected)
) {
return response(404, { code: 'prompt_execution_output_not_found' });
}
const view = availableView(result, expected);
return view
? response(200, view)
: response(503, {
code: 'prompt_execution_output_read_unavailable',
});
} catch {
return response(503, {
code: 'prompt_execution_output_read_unavailable',
});
}
},
});
}
@@ -0,0 +1,400 @@
// Plugin Package Prompt owns bounded, Policy-fenced model execution admission.
import { randomUUID } from 'node:crypto';
import type { SecurityPrincipal } from '@qinglong/runtime-core/security';
import type { ClusterControlAdmissionResponse } from '../../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../../transport/routeRegistry';
export const CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_REQUEST_SCHEMA =
'qinglong/cluster-plugin-package-prompt-execution-request@v2' as const;
export const CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_RESPONSE_SCHEMA =
'qinglong/cluster-plugin-package-prompt-execution-response@v2' as const;
export const CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_EXECUTION_ROUTE =
Object.freeze({
method: 'POST' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/prompts/{promptId}/executions',
operationId: 'prompt.execute',
permission: 'model.invoke',
projectParameter: 'projectId',
});
export const CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS = Object.freeze({
maxParameters: 64,
maxParameterValueBytes: 64 * 1024,
maxOutputTokens: 32_768,
maxExecutionMs: 120_000,
minOutputRetentionMs: 60 * 60_000,
maxOutputRetentionMs: 365 * 24 * 60 * 60_000,
});
export type ClusterPluginPackagePromptOutputIntent =
| Readonly<{ mode: 'live_only' }>
| Readonly<{
mode: 'durable_artifact';
retentionPolicy: Readonly<{
revision: string;
retentionMs: number;
}>;
}>;
export interface ClusterPluginPackagePromptExecutionCommand {
readonly projectId: string;
readonly packageName: string;
readonly promptId: string;
readonly requestId: string;
readonly traceId: string;
readonly auditEventId: string;
readonly principal: Readonly<SecurityPrincipal>;
readonly policyFence: Readonly<{
readonly projectVersion: number;
readonly bindingVersion: number;
}>;
readonly parameters: Readonly<Record<string, string>>;
readonly provider: string;
readonly model: string;
readonly maxOutputTokens: number;
readonly temperature?: number;
readonly deadlineAtMs: number;
readonly plannedAtMs: number;
readonly output?: Readonly<ClusterPluginPackagePromptOutputIntent>;
readonly signal: AbortSignal;
}
export interface ClusterPluginPackagePromptExecutionCapability {
execute(command: Readonly<ClusterPluginPackagePromptExecutionCommand>): Promise<
Readonly<{
readonly status: 'executed' | 'resumed' | 'existing';
readonly admission: Readonly<{
readonly requestId: string;
readonly invocationId: string;
readonly runId: string;
readonly stepRunId: string;
}>;
readonly finalization: Readonly<{ readonly runStatus: string }>;
readonly result: unknown | null;
readonly outputArtifact?: unknown;
}>
>;
}
export interface ClusterPluginPackagePromptExecutionRouteOptions {
readonly maxExecutionMs?: number;
readonly now?: () => number;
readonly createEventId?: () => string;
}
class InvalidPromptExecutionRequestError extends TypeError {}
const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const PACKAGE_NAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
const UUID_V4 =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
function invalid(): never {
throw new InvalidPromptExecutionRequestError();
}
function dataRecord(value: unknown): Record<string, unknown> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
(Object.getPrototypeOf(value) !== Object.prototype &&
Object.getPrototypeOf(value) !== null)
) {
return invalid();
}
return value as Record<string, unknown>;
}
function exactKeys(
value: Record<string, unknown>,
required: readonly string[],
optional: readonly string[] = [],
): void {
const allowed = new Set([...required, ...optional]);
const keys = Object.keys(value);
if (
required.some((key) => !keys.includes(key)) ||
keys.some((key) => !allowed.has(key))
) {
invalid();
}
}
function identifier(value: unknown): string {
if (typeof value !== 'string' || !IDENTIFIER.test(value)) return invalid();
return value;
}
function positiveInteger(value: unknown, maximum: number): number {
if (
typeof value !== 'number' ||
!Number.isSafeInteger(value) ||
value < 1 ||
value > maximum
) {
return invalid();
}
return value as number;
}
function parameters(value: unknown): Readonly<Record<string, string>> {
const record = dataRecord(value);
const names = Object.keys(record).sort();
if (
names.length > CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS.maxParameters
) {
return invalid();
}
const normalized = Object.create(null) as Record<string, string>;
for (const name of names) {
const parameter = record[name];
if (
!/^[A-Za-z][A-Za-z0-9_.-]{0,63}$/.test(name) ||
typeof parameter !== 'string' ||
Buffer.byteLength(parameter, 'utf8') >
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS.maxParameterValueBytes
) {
return invalid();
}
normalized[name] = parameter;
}
return Object.freeze(normalized);
}
function outputIntent(
value: unknown,
): Readonly<ClusterPluginPackagePromptOutputIntent> {
const output = dataRecord(value);
if (output.mode === 'live_only') {
exactKeys(output, ['mode']);
return Object.freeze({ mode: 'live_only' as const });
}
if (output.mode !== 'durable_artifact') return invalid();
exactKeys(output, ['mode', 'retentionPolicy']);
const retention = dataRecord(output.retentionPolicy);
exactKeys(retention, ['retentionMs', 'revision']);
if (
typeof retention.revision !== 'string' ||
!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(retention.revision) ||
!Number.isSafeInteger(retention.retentionMs) ||
(retention.retentionMs as number) <
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS.minOutputRetentionMs ||
(retention.retentionMs as number) >
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS.maxOutputRetentionMs
) {
return invalid();
}
return Object.freeze({
mode: 'durable_artifact' as const,
retentionPolicy: Object.freeze({
revision: retention.revision,
retentionMs: retention.retentionMs as number,
}),
});
}
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function errorCode(error: unknown): string | null {
if (
!error ||
typeof error !== 'object' ||
!('code' in error) ||
typeof error.code !== 'string'
) {
return null;
}
return error.code;
}
function executionError(error: unknown): ClusterControlAdmissionResponse {
const code = errorCode(error);
if (
code === 'PLUGIN_PACKAGE_PROMPT_ADMISSION_NOT_ALLOWED' ||
code === 'PLUGIN_PACKAGE_PROMPT_ADMISSION_CONFLICT' ||
code === 'PLUGIN_PACKAGE_PROMPT_EXECUTION_IN_PROGRESS' ||
code === 'PLUGIN_PACKAGE_PROMPT_RESOLUTION_REQUIRED' ||
code === 'MODEL_INVOCATION_CONFLICT' ||
code === 'MODEL_INVOCATION_REPLAY_BLOCKED'
) {
return response(409, { code: 'prompt_execution_conflict' });
}
if (code === 'MODEL_GATEWAY_BUSY' || code === 'MODEL_PROJECT_QUOTA_EXCEEDED') {
return response(429, { code: 'prompt_execution_capacity_exceeded' });
}
if (code === 'MODEL_POLICY_DENIED' || code === 'MODEL_BUDGET_EXCEEDED') {
return response(422, { code: 'prompt_execution_policy_rejected' });
}
if (code === 'MODEL_INVOCATION_DEADLINE_EXCEEDED') {
return response(504, { code: 'prompt_execution_deadline_exceeded' });
}
if (code === 'MODEL_INVOCATION_ABORTED') {
return response(408, { code: 'prompt_execution_aborted' });
}
if (code === 'PLUGIN_PACKAGE_PROMPT_EXECUTION_PLAN_INVALID') {
return response(400, { code: 'invalid_prompt_execution_request' });
}
return response(503, { code: 'prompt_execution_unavailable' });
}
function parseBody(value: unknown, maximumExecutionMs: number) {
const body = dataRecord(value);
exactKeys(
body,
[
'schema',
'requestId',
'traceId',
'parameters',
'provider',
'model',
'maxOutputTokens',
'timeoutMs',
],
['output', 'temperature'],
);
if (body.schema !== CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_REQUEST_SCHEMA) {
return invalid();
}
const temperature = body.temperature;
const output =
body.output === undefined ? undefined : outputIntent(body.output);
if (
temperature !== undefined &&
(typeof temperature !== 'number' ||
!Number.isFinite(temperature) ||
temperature < 0 ||
temperature > 2)
) {
return invalid();
}
return Object.freeze({
requestId: identifier(body.requestId),
traceId: identifier(body.traceId),
parameters: parameters(body.parameters),
provider: identifier(body.provider),
model: identifier(body.model),
maxOutputTokens: positiveInteger(
body.maxOutputTokens,
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS.maxOutputTokens,
),
timeoutMs: positiveInteger(body.timeoutMs, maximumExecutionMs),
...(output === undefined ? {} : { output }),
...(temperature === undefined ? {} : { temperature }),
});
}
export function createClusterControlPluginPackagePromptExecutionRoute(
capability: ClusterPluginPackagePromptExecutionCapability,
options: ClusterPluginPackagePromptExecutionRouteOptions = {},
): Readonly<ClusterControlRouteDefinition> {
if (!capability || typeof capability.execute !== 'function') {
throw new TypeError('Cluster-control Prompt execution capability is invalid');
}
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('Cluster-control Prompt execution route options are invalid');
}
const maximumExecutionMs =
options.maxExecutionMs ??
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS.maxExecutionMs;
if (
!Number.isSafeInteger(maximumExecutionMs) ||
maximumExecutionMs < 1 ||
maximumExecutionMs >
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_LIMITS.maxExecutionMs ||
(options.now !== undefined && typeof options.now !== 'function') ||
(options.createEventId !== undefined &&
typeof options.createEventId !== 'function')
) {
throw new TypeError('Cluster-control Prompt execution route options are invalid');
}
const now = options.now ?? Date.now;
const createEventId = options.createEventId ?? randomUUID;
return Object.freeze({
...CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_EXECUTION_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
routeParameters: ClusterControlRouteParameters,
) {
let body;
try {
body = parseBody(authorized.request.body, maximumExecutionMs);
} catch {
return response(400, { code: 'invalid_prompt_execution_request' });
}
const projectId = authorized.projectId;
const packageName = routeParameters.packageName;
const promptId = routeParameters.promptId;
const fence = authorized.policyFence;
const plannedAtMs = now();
const auditEventId = createEventId();
if (
projectId === null ||
typeof packageName !== 'string' ||
!PACKAGE_NAME.test(packageName) ||
typeof promptId !== 'string' ||
!IDENTIFIER.test(promptId) ||
!fence ||
fence.bindingVersion === null ||
!Number.isSafeInteger(plannedAtMs) ||
plannedAtMs < 0 ||
typeof auditEventId !== 'string' ||
!UUID_V4.test(auditEventId)
) {
return response(503, { code: 'prompt_execution_unavailable' });
}
try {
const result = await capability.execute({
projectId,
packageName,
promptId,
requestId: body.requestId,
traceId: body.traceId,
auditEventId,
principal: authorized.principal,
policyFence: Object.freeze({
projectVersion: fence.projectVersion,
bindingVersion: fence.bindingVersion,
}),
parameters: body.parameters,
provider: body.provider,
model: body.model,
maxOutputTokens: body.maxOutputTokens,
...(body.temperature === undefined
? {}
: { temperature: body.temperature }),
...(body.output === undefined ? {} : { output: body.output }),
plannedAtMs,
deadlineAtMs: plannedAtMs + body.timeoutMs,
signal: authorized.request.signal,
});
return response(200, {
schema: CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_RESPONSE_SCHEMA,
status: result.status,
replayed: result.status === 'existing',
requestId: result.admission.requestId,
invocationId: result.admission.invocationId,
runId: result.admission.runId,
stepRunId: result.admission.stepRunId,
runStatus: result.finalization.runStatus,
result: result.result,
...(result.outputArtifact === undefined
? {}
: { outputArtifact: result.outputArtifact }),
});
} catch (error) {
return executionError(error);
}
},
});
}
@@ -0,0 +1,232 @@
// Plugin Package Prompt owns its capability-free durable output projection.
import type { SecurityPrincipal } from '@qinglong/runtime-core/security';
import type { ClusterControlAdmissionResponse } from '../../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../../transport/routeRegistry';
export const CLUSTER_PLUGIN_PACKAGE_PROMPT_OUTPUT_READ_RESPONSE_SCHEMA =
'qinglong/cluster-plugin-package-prompt-output-read-response@v1' as const;
export const CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_OUTPUT_READ_ROUTE =
Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/runs/{runId}/prompt-output-artifacts/{artifactId}',
operationId: 'prompt.output.read',
permission: 'artifact.read',
projectParameter: 'projectId',
allowedQuery: Object.freeze(['artifact_digest']),
});
export interface ClusterPluginPackagePromptOutputReadCapability {
read(command: Readonly<{
principal: Readonly<SecurityPrincipal>;
projectId: string;
runId: string;
artifactId: string;
artifactDigest: string;
}>): Promise<unknown>;
}
const IDENTITY = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$/;
const MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/;
const DIGEST = /^[0-9a-f]{64}$/;
const KEY_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
const FINISH_REASONS = new Set([
'stop',
'length',
'content_filter',
'tool_call',
'unknown',
]);
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function exactRecord(
value: unknown,
required: readonly string[],
optional: readonly string[] = [],
): Record<string, unknown> | null {
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
const record = value as Record<string, unknown>;
const keys = Object.keys(record).sort();
const expected = [...required, ...optional.filter((key) => key in record)].sort();
return keys.length === expected.length &&
keys.every((key, index) => key === expected[index])
? record
: null;
}
function nonNegativeInteger(value: unknown): value is number {
return Number.isSafeInteger(value) && (value as number) >= 0;
}
function availableView(
value: unknown,
expected: Readonly<{
projectId: string;
runId: string;
artifactId: string;
artifactDigest: string;
}>,
): Readonly<Record<string, unknown>> | null {
const resultEnvelope = exactRecord(value, [
'schema',
'status',
'reference',
'result',
]);
if (
!resultEnvelope ||
resultEnvelope.schema !==
'qinglong/plugin-package-prompt-output-read-result@v1' ||
resultEnvelope.status !== 'available'
) {
return null;
}
const reference = exactRecord(resultEnvelope.reference, [
'algorithm',
'artifactDigest',
'artifactId',
'contentDigest',
'invocationId',
'keyId',
'outputBytes',
'projectId',
'retentionEligibleAtMs',
'retentionPolicyDigest',
'runId',
'schema',
'stepRunId',
]);
const result = exactRecord(resultEnvelope.result, [
'finishReason',
'model',
'provider',
'text',
'usage',
]);
const usage = result ? exactRecord(result.usage, [
'inputTokens',
'outputTokens',
'totalTokens',
], ['costMicros']) : null;
if (
!reference ||
!result ||
!usage ||
reference.schema !==
'qinglong/plugin-package-prompt-output-artifact-reference@v1' ||
reference.algorithm !== 'aes-256-gcm' ||
reference.projectId !== expected.projectId ||
reference.runId !== expected.runId ||
reference.artifactId !== expected.artifactId ||
reference.artifactDigest !== expected.artifactDigest ||
typeof reference.stepRunId !== 'string' ||
!IDENTITY.test(reference.stepRunId) ||
typeof reference.invocationId !== 'string' ||
!IDENTITY.test(reference.invocationId) ||
typeof reference.contentDigest !== 'string' ||
!DIGEST.test(reference.contentDigest) ||
!nonNegativeInteger(reference.outputBytes) ||
reference.outputBytes > 1024 * 1024 ||
typeof reference.retentionPolicyDigest !== 'string' ||
!DIGEST.test(reference.retentionPolicyDigest) ||
!nonNegativeInteger(reference.retentionEligibleAtMs) ||
typeof reference.keyId !== 'string' ||
!KEY_ID.test(reference.keyId) ||
typeof result.provider !== 'string' ||
!MODEL_ID.test(result.provider) ||
typeof result.model !== 'string' ||
!MODEL_ID.test(result.model) ||
typeof result.text !== 'string' ||
Buffer.byteLength(result.text, 'utf8') > 1024 * 1024 ||
!FINISH_REASONS.has(result.finishReason as string) ||
!nonNegativeInteger(usage.inputTokens) ||
!nonNegativeInteger(usage.outputTokens) ||
!nonNegativeInteger(usage.totalTokens) ||
usage.totalTokens !== usage.inputTokens + usage.outputTokens ||
(usage.costMicros !== undefined && !nonNegativeInteger(usage.costMicros))
) {
return null;
}
return Object.freeze({
schema: CLUSTER_PLUGIN_PACKAGE_PROMPT_OUTPUT_READ_RESPONSE_SCHEMA,
status: 'available',
reference: Object.freeze({ ...reference }),
result: Object.freeze({
provider: result.provider,
model: result.model,
text: result.text,
finishReason: result.finishReason,
usage: Object.freeze({ ...usage }),
}),
});
}
export function createClusterControlPluginPackagePromptOutputReadRoute(
capability: ClusterPluginPackagePromptOutputReadCapability,
): Readonly<ClusterControlRouteDefinition> {
if (!capability || typeof capability.read !== 'function') {
throw new TypeError('Cluster-control Prompt output read capability is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_PLUGIN_PACKAGE_PROMPT_OUTPUT_READ_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
const artifactDigestValues =
authorized.request.query.artifact_digest;
if (
authorized.request.body !== null ||
authorized.projectId === null ||
typeof parameters.runId !== 'string' ||
!RUN_ID.test(parameters.runId) ||
typeof parameters.artifactId !== 'string' ||
!IDENTITY.test(parameters.artifactId) ||
!Array.isArray(artifactDigestValues) ||
artifactDigestValues.length !== 1 ||
typeof artifactDigestValues[0] !== 'string' ||
!DIGEST.test(artifactDigestValues[0])
) {
return response(400, { code: 'invalid_prompt_output_read_request' });
}
const expected = Object.freeze({
projectId: authorized.projectId,
runId: parameters.runId,
artifactId: parameters.artifactId,
artifactDigest: artifactDigestValues[0],
});
try {
const result = await capability.read({
principal: authorized.principal,
...expected,
});
const notFound = exactRecord(result, ['schema', 'status']);
if (
notFound &&
notFound.schema ===
'qinglong/plugin-package-prompt-output-read-result@v1' &&
notFound.status === 'not_found'
) {
return response(404, { code: 'prompt_output_not_found' });
}
const view = availableView(result, expected);
return view
? response(200, view)
: response(503, { code: 'prompt_output_read_unavailable' });
} catch {
return response(503, { code: 'prompt_output_read_unavailable' });
}
},
});
}
@@ -0,0 +1,6 @@
// Plugin Package Prompt owns its stable execution and output-read route surface.
export * from './pluginPackagePromptExecutionRoute';
export * from './pluginPackagePromptCatalogRoute';
export * from './pluginPackagePromptExecutionInspectionRoute';
export * from './pluginPackagePromptExecutionOutputReadRoute';
export * from './pluginPackagePromptOutputReadRoute';
@@ -0,0 +1,535 @@
// Plugin Package Workflow owns inspection and durable authorized admission.
import type {
SecurityPolicyFence,
SecurityPrincipal,
} from '@qinglong/runtime-core/security';
import { normalizeSecurityAuditRecord } from '@qinglong/runtime-core/security-audit';
import type {
PluginPackageAutomationPublication,
PluginPackageAutomationPublicationRepository,
} from '@qinglong/runtime-core/plugin-package-automation-publication';
import type {
PluginPackageMaterializedRevision,
PluginPackageMaterializedRevisionRepository,
PluginPackageWorkflowResource,
} from '@qinglong/runtime-core/plugin-package-resource-materialization';
import type {
PluginPackageWorkflowAdministrationRepository,
PluginPackageWorkflowRunEventListRepository,
PluginPackageWorkflowRunEventListResult,
PluginPackageWorkflowRunInspectionRepository,
PluginPackageWorkflowRunInspectionResult,
PluginPackageWorkflowRunListRepository,
PluginPackageWorkflowRunListResult,
PluginPackageWorkflowStepRunListRepository,
PluginPackageWorkflowStepRunListResult,
} from '@qinglong/runtime-core/plugin-package-workflow-administration';
import type {
ClusterRunCancellationRepository,
ClusterRunCancellationResult,
} from '@qinglong/runtime-core/cluster-run-cancellation';
import {
createPluginPackageWorkflowExecutionPlan,
type PluginPackageWorkflowAdmissionReceipt,
type PluginPackageWorkflowExecutionPlan,
} from '@qinglong/runtime-core/plugin-package-workflow-execution-plan';
import {
TaskSpecSemanticRegistry,
createBuiltInTaskSpecSemanticRegistry,
} from '@qinglong/runtime-core/task-spec-semantic';
export interface ClusterPluginPackageWorkflowSummary {
readonly id: string;
readonly name: string;
readonly enabled: boolean;
readonly steps: readonly Readonly<{
id: string;
task: string;
needs: readonly string[];
}>[];
}
export interface StartClusterPluginPackageWorkflowCommand {
readonly projectId: string;
readonly packageName: string;
readonly workflowId: string;
readonly planId: string;
readonly runId: string;
readonly stepRunIds: Readonly<Record<string, string>>;
readonly principal: Readonly<SecurityPrincipal>;
readonly policyFence: Readonly<SecurityPolicyFence>;
readonly plannedAtMs: number;
}
export interface CancelClusterPluginPackageWorkflowCommand {
readonly projectId: string;
readonly packageName: string;
readonly workflowId: string;
readonly runId: string;
readonly mutationId: string;
readonly eventId: string;
readonly principal: Readonly<SecurityPrincipal>;
readonly policyFence: Readonly<SecurityPolicyFence>;
}
export interface InspectClusterPluginPackageWorkflowRunCommand {
readonly projectId: string;
readonly packageName: string;
readonly workflowId: string;
readonly runId: string;
readonly requestId: string;
readonly auditEventId: string;
readonly principal: Readonly<SecurityPrincipal>;
readonly policyFence: Readonly<SecurityPolicyFence>;
readonly observedAtMs: number;
}
export interface ListClusterPluginPackageWorkflowRunsCommand {
readonly projectId: string;
readonly packageName: string;
readonly workflowId: string;
readonly limit: number;
readonly after: Readonly<{ admittedAtMs: number; runId: string }> | null;
readonly requestId: string;
readonly auditEventId: string;
readonly principal: Readonly<SecurityPrincipal>;
readonly policyFence: Readonly<SecurityPolicyFence>;
readonly observedAtMs: number;
}
export interface ListClusterPluginPackageWorkflowStepRunsCommand {
readonly projectId: string;
readonly packageName: string;
readonly workflowId: string;
readonly runId: string;
readonly limit: number;
readonly after: Readonly<{ stepKey: string; id: string }> | null;
readonly requestId: string;
readonly auditEventId: string;
readonly principal: Readonly<SecurityPrincipal>;
readonly policyFence: Readonly<SecurityPolicyFence>;
readonly observedAtMs: number;
}
export interface ListClusterPluginPackageWorkflowRunEventsCommand {
readonly projectId: string;
readonly packageName: string;
readonly workflowId: string;
readonly runId: string;
readonly limit: number;
readonly afterSequence: number;
readonly requestId: string;
readonly auditEventId: string;
readonly principal: Readonly<SecurityPrincipal>;
readonly policyFence: Readonly<SecurityPolicyFence>;
readonly observedAtMs: number;
}
export interface ClusterPluginPackageWorkflowAdministrationCapability {
inspect(
projectId: string,
packageName: string,
): Promise<
Readonly<{
found: boolean;
publicationState: PluginPackageAutomationPublication['state'] | null;
workflows: readonly Readonly<ClusterPluginPackageWorkflowSummary>[];
}>
>;
start(command: Readonly<StartClusterPluginPackageWorkflowCommand>): Promise<
Readonly<{
status: 'created' | 'existing';
plan: Readonly<PluginPackageWorkflowExecutionPlan>;
receipt: Readonly<PluginPackageWorkflowAdmissionReceipt>;
}>
>;
cancel(
command: Readonly<CancelClusterPluginPackageWorkflowCommand>,
): Promise<Readonly<ClusterRunCancellationResult>>;
inspectRun(
command: Readonly<InspectClusterPluginPackageWorkflowRunCommand>,
): Promise<Readonly<PluginPackageWorkflowRunInspectionResult>>;
listRuns(
command: Readonly<ListClusterPluginPackageWorkflowRunsCommand>,
): Promise<Readonly<PluginPackageWorkflowRunListResult>>;
listStepRuns(
command: Readonly<ListClusterPluginPackageWorkflowStepRunsCommand>,
): Promise<Readonly<PluginPackageWorkflowStepRunListResult>>;
listRunEvents(
command: Readonly<ListClusterPluginPackageWorkflowRunEventsCommand>,
): Promise<Readonly<PluginPackageWorkflowRunEventListResult>>;
}
export class ClusterPluginPackageWorkflowNotFoundError extends Error {
readonly code = 'CLUSTER_PLUGIN_PACKAGE_WORKFLOW_NOT_FOUND';
constructor() {
super('Active Plugin Package Workflow is not available');
this.name = 'ClusterPluginPackageWorkflowNotFoundError';
}
}
export class ClusterPluginPackageWorkflowConflictError extends Error {
readonly code = 'CLUSTER_PLUGIN_PACKAGE_WORKFLOW_CONFLICT';
constructor() {
super('Plugin Package Workflow request conflicts with durable identity');
this.name = 'ClusterPluginPackageWorkflowConflictError';
}
}
export class ClusterPluginPackageWorkflowUnavailableError extends Error {
readonly code = 'CLUSTER_PLUGIN_PACKAGE_WORKFLOW_UNAVAILABLE';
constructor() {
super('Plugin Package Workflow administration is unavailable');
this.name = 'ClusterPluginPackageWorkflowUnavailableError';
}
}
function summary(
workflow: Readonly<PluginPackageWorkflowResource>,
): Readonly<ClusterPluginPackageWorkflowSummary> {
return Object.freeze({
id: workflow.id,
name: workflow.name,
enabled: workflow.enabled,
steps: Object.freeze(
workflow.steps.map((step) =>
Object.freeze({
id: step.id,
task: step.task,
needs: Object.freeze([...step.needs]),
}),
),
),
});
}
function sameReplay(
plan: Readonly<PluginPackageWorkflowExecutionPlan>,
command: Readonly<StartClusterPluginPackageWorkflowCommand>,
): boolean {
const requested = Object.entries(command.stepRunIds).sort(([a], [b]) =>
a.localeCompare(b),
);
const stored = plan.steps
.map((step) => [step.stepKey, step.stepRunId] as const)
.sort(([a], [b]) => a.localeCompare(b));
return (
plan.planId === command.planId &&
plan.runId === command.runId &&
plan.target.projectId === command.projectId &&
plan.target.packageName === command.packageName &&
plan.target.workflowId === command.workflowId &&
stored.length === requested.length &&
stored.every(
([key, id], index) =>
key === requested[index]?.[0] && id === requested[index]?.[1],
)
);
}
export function createClusterPluginPackageWorkflowAdministrationCapability(
publications: Pick<
PluginPackageAutomationPublicationRepository,
'findCurrent'
>,
revisions: Pick<PluginPackageMaterializedRevisionRepository, 'find'>,
admissions: PluginPackageWorkflowAdministrationRepository,
runInspections: PluginPackageWorkflowRunInspectionRepository,
runLists: PluginPackageWorkflowRunListRepository,
stepRunLists: PluginPackageWorkflowStepRunListRepository,
runEventLists: PluginPackageWorkflowRunEventListRepository,
cancellations: ClusterRunCancellationRepository,
taskSpecSemanticRegistry: TaskSpecSemanticRegistry = createBuiltInTaskSpecSemanticRegistry(),
): ClusterPluginPackageWorkflowAdministrationCapability {
if (
!publications ||
typeof publications.findCurrent !== 'function' ||
!revisions ||
typeof revisions.find !== 'function' ||
!admissions ||
typeof admissions.findPlanByPlanId !== 'function' ||
typeof admissions.admitAuthorized !== 'function' ||
!runInspections ||
typeof runInspections.inspectRunAuthorized !== 'function' ||
!runLists ||
typeof runLists.listRunsAuthorized !== 'function' ||
!stepRunLists ||
typeof stepRunLists.listStepRunsAuthorized !== 'function' ||
!runEventLists ||
typeof runEventLists.listRunEventsAuthorized !== 'function' ||
!cancellations ||
typeof cancellations.requestUserCancellation !== 'function' ||
!(taskSpecSemanticRegistry instanceof TaskSpecSemanticRegistry)
) {
throw new TypeError(
'Cluster Plugin Package Workflow administration dependencies are invalid',
);
}
async function currentTarget(
projectId: string,
packageName: string,
): Promise<Readonly<{
publication: Readonly<PluginPackageAutomationPublication>;
revision: Readonly<PluginPackageMaterializedRevision>;
}> | null> {
try {
const publication = await publications.findCurrent(
projectId,
packageName,
);
if (!publication) return null;
const revision = await revisions.find(
publication.target.generationDigest,
);
if (
!revision ||
revision.revisionDigest !==
publication.target.materializedRevisionDigest
) {
throw new ClusterPluginPackageWorkflowUnavailableError();
}
return Object.freeze({ publication, revision });
} catch (error) {
if (error instanceof ClusterPluginPackageWorkflowUnavailableError) {
throw error;
}
throw new ClusterPluginPackageWorkflowUnavailableError();
}
}
return Object.freeze({
async inspect(projectId: string, packageName: string) {
const target = await currentTarget(projectId, packageName);
return target
? Object.freeze({
found: true,
publicationState: target.publication.state,
workflows: Object.freeze(
target.publication.definitions.workflows.map(summary),
),
})
: Object.freeze({
found: false,
publicationState: null,
workflows: Object.freeze([]),
});
},
async start(command: Readonly<StartClusterPluginPackageWorkflowCommand>) {
let plan = await admissions.findPlanByPlanId(command.planId);
if (plan) {
if (!sameReplay(plan, command)) {
throw new ClusterPluginPackageWorkflowConflictError();
}
} else {
const target = await currentTarget(
command.projectId,
command.packageName,
);
const workflow = target?.publication.definitions.workflows.find(
({ id }) => id === command.workflowId,
);
if (
!target ||
target.publication.state !== 'active' ||
!workflow?.enabled
) {
throw new ClusterPluginPackageWorkflowNotFoundError();
}
try {
plan = createPluginPackageWorkflowExecutionPlan({
planId: command.planId,
runId: command.runId,
workflowId: command.workflowId,
stepRunIds: command.stepRunIds,
publication: target.publication,
revision: target.revision,
taskSpecSemanticRegistry,
plannedAtMs: command.plannedAtMs,
});
} catch {
throw new ClusterPluginPackageWorkflowConflictError();
}
}
if (command.policyFence.bindingVersion === null) {
throw new ClusterPluginPackageWorkflowUnavailableError();
}
const admitted = await admissions.admitAuthorized({
plan,
actor: command.principal.subject,
fence: {
projectVersion: command.policyFence.projectVersion,
bindingVersion: command.policyFence.bindingVersion,
},
audit: normalizeSecurityAuditRecord({
eventId: command.planId,
requestId: command.planId,
operationId: 'workflow.start',
projectId: command.projectId,
subject: command.principal.subject,
authenticationId: command.principal.authenticationId,
outcome: 'allowed',
reasons: ['project_policy_allowed'],
fence: command.policyFence,
occurredAtMs: plan.plannedAtMs,
}),
});
return Object.freeze({
status: admitted.status,
plan,
receipt: admitted.receipt,
});
},
async cancel(command: Readonly<CancelClusterPluginPackageWorkflowCommand>) {
if (command.policyFence.bindingVersion === null) {
throw new ClusterPluginPackageWorkflowUnavailableError();
}
return cancellations.requestUserCancellation({
projectId: command.projectId,
runId: command.runId,
mutationId: command.mutationId,
eventId: command.eventId,
subject: command.principal.subject,
policyFence: command.policyFence,
workflowTarget: {
packageName: command.packageName,
workflowId: command.workflowId,
},
});
},
async inspectRun(
command: Readonly<InspectClusterPluginPackageWorkflowRunCommand>,
) {
if (command.policyFence.bindingVersion === null) {
throw new ClusterPluginPackageWorkflowUnavailableError();
}
return runInspections.inspectRunAuthorized({
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
actor: command.principal.subject,
fence: {
projectVersion: command.policyFence.projectVersion,
bindingVersion: command.policyFence.bindingVersion,
},
audit: normalizeSecurityAuditRecord({
eventId: command.auditEventId,
requestId: command.requestId,
operationId: 'workflow.run.read',
projectId: command.projectId,
subject: command.principal.subject,
authenticationId: command.principal.authenticationId,
outcome: 'allowed',
reasons: ['project_policy_allowed'],
fence: command.policyFence,
occurredAtMs: command.observedAtMs,
}),
});
},
async listRuns(
command: Readonly<ListClusterPluginPackageWorkflowRunsCommand>,
) {
if (command.policyFence.bindingVersion === null) {
throw new ClusterPluginPackageWorkflowUnavailableError();
}
return runLists.listRunsAuthorized({
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
limit: command.limit,
after: command.after,
actor: command.principal.subject,
fence: {
projectVersion: command.policyFence.projectVersion,
bindingVersion: command.policyFence.bindingVersion,
},
audit: normalizeSecurityAuditRecord({
eventId: command.auditEventId,
requestId: command.requestId,
operationId: 'workflow.run.list',
projectId: command.projectId,
subject: command.principal.subject,
authenticationId: command.principal.authenticationId,
outcome: 'allowed',
reasons: ['project_policy_allowed'],
fence: command.policyFence,
occurredAtMs: command.observedAtMs,
}),
});
},
async listStepRuns(
command: Readonly<ListClusterPluginPackageWorkflowStepRunsCommand>,
) {
if (command.policyFence.bindingVersion === null) {
throw new ClusterPluginPackageWorkflowUnavailableError();
}
return stepRunLists.listStepRunsAuthorized({
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
limit: command.limit,
after: command.after,
actor: command.principal.subject,
fence: {
projectVersion: command.policyFence.projectVersion,
bindingVersion: command.policyFence.bindingVersion,
},
audit: normalizeSecurityAuditRecord({
eventId: command.auditEventId,
requestId: command.requestId,
operationId: 'workflow.step.list',
projectId: command.projectId,
subject: command.principal.subject,
authenticationId: command.principal.authenticationId,
outcome: 'allowed',
reasons: ['project_policy_allowed'],
fence: command.policyFence,
occurredAtMs: command.observedAtMs,
}),
});
},
async listRunEvents(
command: Readonly<ListClusterPluginPackageWorkflowRunEventsCommand>,
) {
if (command.policyFence.bindingVersion === null) {
throw new ClusterPluginPackageWorkflowUnavailableError();
}
return runEventLists.listRunEventsAuthorized({
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
limit: command.limit,
afterSequence: command.afterSequence,
actor: command.principal.subject,
fence: {
projectVersion: command.policyFence.projectVersion,
bindingVersion: command.policyFence.bindingVersion,
},
audit: normalizeSecurityAuditRecord({
eventId: command.auditEventId,
requestId: command.requestId,
operationId: 'workflow.event.list',
projectId: command.projectId,
subject: command.principal.subject,
authenticationId: command.principal.authenticationId,
outcome: 'allowed',
reasons: ['project_policy_allowed'],
fence: command.policyFence,
occurredAtMs: command.observedAtMs,
}),
});
},
});
}
@@ -0,0 +1,707 @@
// Plugin Package Workflow owns its bounded inspect/start/cancel transport adapter.
import { randomUUID } from 'node:crypto';
import {
CLUSTER_RUN_CANCELLATION_SCHEMA,
createClusterRunCancellationResponseBody,
parseClusterRunCancellationRequestBody,
} from '@qinglong/runtime-core/cluster-run-cancellation';
import {
DEFAULT_PLUGIN_PACKAGE_WORKFLOW_RUN_EVENT_LIST_PAGE_SIZE,
DEFAULT_PLUGIN_PACKAGE_WORKFLOW_RUN_LIST_PAGE_SIZE,
DEFAULT_PLUGIN_PACKAGE_WORKFLOW_STEP_RUN_LIST_PAGE_SIZE,
MAX_PLUGIN_PACKAGE_WORKFLOW_RUN_EVENT_LIST_PAGE_SIZE,
MAX_PLUGIN_PACKAGE_WORKFLOW_RUN_LIST_PAGE_SIZE,
MAX_PLUGIN_PACKAGE_WORKFLOW_STEP_RUN_LIST_PAGE_SIZE,
} from '@qinglong/runtime-core/plugin-package-workflow-administration';
import type { ClusterControlAdmissionResponse } from '../../transport/httpSurface';
import type { ClusterPluginPackageWorkflowAdministrationCapability } from './pluginPackageWorkflowAdministration';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../../transport/routeRegistry';
export const CLUSTER_PLUGIN_PACKAGE_WORKFLOW_LIST_RESPONSE_SCHEMA =
'qinglong/cluster-plugin-package-workflow-list@v1' as const;
export const CLUSTER_PLUGIN_PACKAGE_WORKFLOW_START_REQUEST_SCHEMA =
'qinglong/cluster-plugin-package-workflow-start-request@v1' as const;
export const CLUSTER_PLUGIN_PACKAGE_WORKFLOW_START_RESPONSE_SCHEMA =
'qinglong/cluster-plugin-package-workflow-start-response@v1' as const;
const UUID_V4 =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
const PACKAGE_NAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
const RESOURCE_ID = /^[a-z][a-z0-9-]{0,62}$/;
function parseRunListQuery(
query: Readonly<Record<string, readonly string[]>>,
): Readonly<{
limit: number;
after: Readonly<{ admittedAtMs: number; runId: string }> | null;
}> {
const limitValues = query.limit;
const admittedAtValues = query.after_admitted_at_ms;
const runIdValues = query.after_run_id;
if (
(limitValues !== undefined && limitValues.length !== 1) ||
(admittedAtValues !== undefined && admittedAtValues.length !== 1) ||
(runIdValues !== undefined && runIdValues.length !== 1) ||
(admittedAtValues === undefined) !== (runIdValues === undefined)
) {
throw new TypeError();
}
const limit =
limitValues === undefined
? DEFAULT_PLUGIN_PACKAGE_WORKFLOW_RUN_LIST_PAGE_SIZE
: Number(limitValues[0]);
if (
!Number.isSafeInteger(limit) ||
limit < 1 ||
limit > MAX_PLUGIN_PACKAGE_WORKFLOW_RUN_LIST_PAGE_SIZE ||
(limitValues !== undefined && String(limit) !== limitValues[0])
) {
throw new TypeError();
}
if (admittedAtValues === undefined || runIdValues === undefined) {
return Object.freeze({ limit, after: null });
}
const admittedAtMs = Number(admittedAtValues[0]);
const runId = runIdValues[0]!;
if (
!Number.isSafeInteger(admittedAtMs) ||
admittedAtMs < 0 ||
String(admittedAtMs) !== admittedAtValues[0] ||
!UUID_V4.test(runId)
) {
throw new TypeError();
}
return Object.freeze({
limit,
after: Object.freeze({ admittedAtMs, runId }),
});
}
function parseStepRunListQuery(
query: Readonly<Record<string, readonly string[]>>,
): Readonly<{
limit: number;
after: Readonly<{ stepKey: string; id: string }> | null;
}> {
const limitValues = query.limit;
const stepKeyValues = query.after_step_key;
const stepRunIdValues = query.after_step_run_id;
if (
(limitValues !== undefined && limitValues.length !== 1) ||
(stepKeyValues !== undefined && stepKeyValues.length !== 1) ||
(stepRunIdValues !== undefined && stepRunIdValues.length !== 1) ||
(stepKeyValues === undefined) !== (stepRunIdValues === undefined)
) {
throw new TypeError();
}
const limit =
limitValues === undefined
? DEFAULT_PLUGIN_PACKAGE_WORKFLOW_STEP_RUN_LIST_PAGE_SIZE
: Number(limitValues[0]);
if (
!Number.isSafeInteger(limit) ||
limit < 1 ||
limit > MAX_PLUGIN_PACKAGE_WORKFLOW_STEP_RUN_LIST_PAGE_SIZE ||
(limitValues !== undefined && String(limit) !== limitValues[0])
) {
throw new TypeError();
}
if (stepKeyValues === undefined || stepRunIdValues === undefined) {
return Object.freeze({ limit, after: null });
}
const stepKey = stepKeyValues[0]!;
const id = stepRunIdValues[0]!;
if (!RESOURCE_ID.test(stepKey) || !UUID_V4.test(id)) {
throw new TypeError();
}
return Object.freeze({
limit,
after: Object.freeze({ stepKey, id }),
});
}
function parseRunEventListQuery(
query: Readonly<Record<string, readonly string[]>>,
): Readonly<{ limit: number; afterSequence: number }> {
const limitValues = query.limit;
const afterValues = query.after_sequence;
if (
(limitValues !== undefined && limitValues.length !== 1) ||
(afterValues !== undefined && afterValues.length !== 1)
) {
throw new TypeError();
}
const limit =
limitValues === undefined
? DEFAULT_PLUGIN_PACKAGE_WORKFLOW_RUN_EVENT_LIST_PAGE_SIZE
: Number(limitValues[0]);
const afterSequence = afterValues === undefined ? 0 : Number(afterValues[0]);
if (
!Number.isSafeInteger(limit) ||
limit < 1 ||
limit > MAX_PLUGIN_PACKAGE_WORKFLOW_RUN_EVENT_LIST_PAGE_SIZE ||
(limitValues !== undefined && String(limit) !== limitValues[0]) ||
!Number.isSafeInteger(afterSequence) ||
afterSequence < 0 ||
(afterValues !== undefined && String(afterSequence) !== afterValues[0])
) {
throw new TypeError();
}
return Object.freeze({ limit, afterSequence });
}
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function parseBody(value: unknown): Readonly<{
planId: string;
runId: string;
stepRunIds: Readonly<Record<string, string>>;
}> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new TypeError();
}
const body = value as Record<string, unknown>;
if (
Object.keys(body).sort().join(',') !== 'planId,runId,schema,stepRunIds' ||
body.schema !== CLUSTER_PLUGIN_PACKAGE_WORKFLOW_START_REQUEST_SCHEMA ||
typeof body.planId !== 'string' ||
!UUID_V4.test(body.planId) ||
typeof body.runId !== 'string' ||
!UUID_V4.test(body.runId) ||
!body.stepRunIds ||
typeof body.stepRunIds !== 'object' ||
Array.isArray(body.stepRunIds) ||
Object.getPrototypeOf(body.stepRunIds) !== Object.prototype
) {
throw new TypeError();
}
const entries = Object.entries(body.stepRunIds as Record<string, unknown>);
if (
entries.length < 1 ||
entries.length > 128 ||
entries.some(
([key, id]) =>
!RESOURCE_ID.test(key) || typeof id !== 'string' || !UUID_V4.test(id),
) ||
new Set(entries.map(([, id]) => id)).size !== entries.length
) {
throw new TypeError();
}
return Object.freeze({
planId: body.planId,
runId: body.runId,
stepRunIds: Object.freeze(
Object.fromEntries(entries) as Record<string, string>,
),
});
}
function errorResponse(error: unknown): ClusterControlAdmissionResponse {
const code =
error && typeof error === 'object' && 'code' in error
? (error as { code?: unknown }).code
: null;
if (code === 'CLUSTER_PLUGIN_PACKAGE_WORKFLOW_NOT_FOUND') {
return response(404, { code: 'workflow_not_found' });
}
if (
code === 'CLUSTER_PLUGIN_PACKAGE_WORKFLOW_CONFLICT' ||
code === 'PLUGIN_PACKAGE_WORKFLOW_ADMINISTRATION_MUTATION_CONFLICT' ||
code === 'PLUGIN_PACKAGE_WORKFLOW_ADMISSION_CONFLICT'
) {
return response(409, { code: 'workflow_start_conflict' });
}
if (
code ===
'PLUGIN_PACKAGE_WORKFLOW_ADMINISTRATION_AUTHORIZATION_FENCE_CONFLICT'
) {
return response(409, { code: 'authorization_fence_changed' });
}
if (code === 'CLUSTER_RUN_CANCELLATION_NOT_FOUND') {
return response(404, { code: 'workflow_run_not_found' });
}
if (code === 'CLUSTER_RUN_CANCELLATION_FENCE_REJECTED') {
const candidateReason =
error && typeof error === 'object' && 'reason' in error
? (error as { reason?: unknown }).reason
: null;
const reason =
candidateReason === 'authorization_changed' ||
candidateReason === 'project_mismatch' ||
candidateReason === 'state_mismatch'
? candidateReason
: 'state_mismatch';
return response(409, {
code: 'workflow_cancellation_fence_rejected',
reason,
});
}
return response(503, { code: 'workflow_administration_unavailable' });
}
function runInspectionErrorResponse(
error: unknown,
): ClusterControlAdmissionResponse {
const code =
error && typeof error === 'object' && 'code' in error
? (error as { code?: unknown }).code
: null;
if (
code ===
'PLUGIN_PACKAGE_WORKFLOW_ADMINISTRATION_AUTHORIZATION_FENCE_CONFLICT'
) {
return response(409, { code: 'authorization_fence_changed' });
}
return response(503, { code: 'workflow_run_query_unavailable' });
}
function runListErrorResponse(error: unknown): ClusterControlAdmissionResponse {
const code =
error && typeof error === 'object' && 'code' in error
? (error as { code?: unknown }).code
: null;
if (
code ===
'PLUGIN_PACKAGE_WORKFLOW_ADMINISTRATION_AUTHORIZATION_FENCE_CONFLICT'
) {
return response(409, { code: 'authorization_fence_changed' });
}
return response(503, { code: 'workflow_run_list_unavailable' });
}
function stepRunListErrorResponse(
error: unknown,
): ClusterControlAdmissionResponse {
const code =
error && typeof error === 'object' && 'code' in error
? (error as { code?: unknown }).code
: null;
if (
code ===
'PLUGIN_PACKAGE_WORKFLOW_ADMINISTRATION_AUTHORIZATION_FENCE_CONFLICT'
) {
return response(409, { code: 'authorization_fence_changed' });
}
return response(503, { code: 'workflow_step_run_query_unavailable' });
}
function runEventListErrorResponse(
error: unknown,
): ClusterControlAdmissionResponse {
const code =
error && typeof error === 'object' && 'code' in error
? (error as { code?: unknown }).code
: null;
if (
code ===
'PLUGIN_PACKAGE_WORKFLOW_ADMINISTRATION_AUTHORIZATION_FENCE_CONFLICT'
) {
return response(409, { code: 'authorization_fence_changed' });
}
return response(503, { code: 'workflow_run_event_query_unavailable' });
}
export function createClusterControlPluginPackageWorkflowRoutes(
capability: ClusterPluginPackageWorkflowAdministrationCapability,
now: () => number = Date.now,
createEventId: () => string = randomUUID,
): readonly Readonly<ClusterControlRouteDefinition>[] {
if (
!capability ||
typeof capability.inspect !== 'function' ||
typeof capability.inspectRun !== 'function' ||
typeof capability.listRuns !== 'function' ||
typeof capability.listStepRuns !== 'function' ||
typeof capability.listRunEvents !== 'function' ||
typeof capability.start !== 'function' ||
typeof capability.cancel !== 'function' ||
typeof now !== 'function' ||
typeof createEventId !== 'function'
) {
throw new TypeError('Cluster-control Workflow capability is invalid');
}
const common = {
projectParameter: 'projectId' as const,
};
return Object.freeze([
Object.freeze({
...common,
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/workflows',
operationId: 'workflow.read',
permission: 'run.read',
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName)
) {
return response(503, { code: 'workflow_administration_unavailable' });
}
try {
const result = await capability.inspect(
authorized.projectId,
parameters.packageName,
);
return response(200, {
schema: CLUSTER_PLUGIN_PACKAGE_WORKFLOW_LIST_RESPONSE_SCHEMA,
...result,
});
} catch (error) {
return errorResponse(error);
}
},
}),
Object.freeze({
...common,
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/workflows/{workflowId}/runs',
operationId: 'workflow.run.list',
permission: 'run.read',
allowedQuery: Object.freeze([
'after_admitted_at_ms',
'after_run_id',
'limit',
]),
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
let page;
try {
page = parseRunListQuery(authorized.request.query);
} catch {
return response(400, { code: 'invalid_workflow_run_query' });
}
const observedAtMs = now();
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.workflowId !== 'string' ||
!RESOURCE_ID.test(parameters.workflowId) ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null ||
!Number.isSafeInteger(observedAtMs) ||
observedAtMs < 0
) {
return response(503, { code: 'workflow_run_list_unavailable' });
}
try {
const result = await capability.listRuns({
projectId: authorized.projectId,
packageName: parameters.packageName,
workflowId: parameters.workflowId,
limit: page.limit,
after: page.after,
requestId: authorized.request.requestId,
auditEventId: createEventId(),
principal: authorized.principal,
policyFence: authorized.policyFence,
observedAtMs,
});
return response(200, { ...result });
} catch (error) {
return runListErrorResponse(error);
}
},
}),
Object.freeze({
...common,
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/workflows/{workflowId}/runs/{runId}',
operationId: 'workflow.run.read',
permission: 'run.read',
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
const observedAtMs = now();
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.workflowId !== 'string' ||
!RESOURCE_ID.test(parameters.workflowId) ||
typeof parameters.runId !== 'string' ||
!UUID_V4.test(parameters.runId) ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null ||
!Number.isSafeInteger(observedAtMs) ||
observedAtMs < 0
) {
return response(503, { code: 'workflow_run_query_unavailable' });
}
try {
const result = await capability.inspectRun({
projectId: authorized.projectId,
packageName: parameters.packageName,
workflowId: parameters.workflowId,
runId: parameters.runId,
requestId: authorized.request.requestId,
auditEventId: createEventId(),
principal: authorized.principal,
policyFence: authorized.policyFence,
observedAtMs,
});
return result.found
? response(200, { ...result })
: response(404, { code: 'workflow_run_not_found' });
} catch (error) {
return runInspectionErrorResponse(error);
}
},
}),
Object.freeze({
...common,
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/workflows/{workflowId}/runs/{runId}/steps',
operationId: 'workflow.step.list',
permission: 'run.read',
allowedQuery: Object.freeze([
'after_step_key',
'after_step_run_id',
'limit',
]),
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
let page;
try {
page = parseStepRunListQuery(authorized.request.query);
} catch {
return response(400, { code: 'invalid_step_run_query' });
}
const observedAtMs = now();
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.workflowId !== 'string' ||
!RESOURCE_ID.test(parameters.workflowId) ||
typeof parameters.runId !== 'string' ||
!UUID_V4.test(parameters.runId) ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null ||
!Number.isSafeInteger(observedAtMs) ||
observedAtMs < 0
) {
return response(503, { code: 'workflow_step_run_query_unavailable' });
}
try {
const result = await capability.listStepRuns({
projectId: authorized.projectId,
packageName: parameters.packageName,
workflowId: parameters.workflowId,
runId: parameters.runId,
limit: page.limit,
after: page.after,
requestId: authorized.request.requestId,
auditEventId: createEventId(),
principal: authorized.principal,
policyFence: authorized.policyFence,
observedAtMs,
});
return result.found
? response(200, { ...result })
: response(404, { code: 'workflow_run_not_found' });
} catch (error) {
return stepRunListErrorResponse(error);
}
},
}),
Object.freeze({
...common,
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/workflows/{workflowId}/runs/{runId}/events',
operationId: 'workflow.event.list',
permission: 'run.read',
allowedQuery: Object.freeze(['after_sequence', 'limit']),
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
let page;
try {
page = parseRunEventListQuery(authorized.request.query);
} catch {
return response(400, { code: 'invalid_run_event_query' });
}
const observedAtMs = now();
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.workflowId !== 'string' ||
!RESOURCE_ID.test(parameters.workflowId) ||
typeof parameters.runId !== 'string' ||
!UUID_V4.test(parameters.runId) ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null ||
!Number.isSafeInteger(observedAtMs) ||
observedAtMs < 0
) {
return response(503, {
code: 'workflow_run_event_query_unavailable',
});
}
try {
const result = await capability.listRunEvents({
projectId: authorized.projectId,
packageName: parameters.packageName,
workflowId: parameters.workflowId,
runId: parameters.runId,
limit: page.limit,
afterSequence: page.afterSequence,
requestId: authorized.request.requestId,
auditEventId: createEventId(),
principal: authorized.principal,
policyFence: authorized.policyFence,
observedAtMs,
});
return result.found
? response(200, { ...result })
: response(404, { code: 'workflow_run_not_found' });
} catch (error) {
return runEventListErrorResponse(error);
}
},
}),
Object.freeze({
...common,
method: 'POST' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/workflows/{workflowId}/runs',
operationId: 'workflow.start',
permission: 'run.start',
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
let body;
try {
body = parseBody(authorized.request.body);
} catch {
return response(400, { code: 'invalid_workflow_start_request' });
}
const plannedAtMs = now();
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.workflowId !== 'string' ||
!RESOURCE_ID.test(parameters.workflowId) ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null ||
!Number.isSafeInteger(plannedAtMs) ||
plannedAtMs < 0
) {
return response(503, { code: 'workflow_administration_unavailable' });
}
try {
const result = await capability.start({
projectId: authorized.projectId,
packageName: parameters.packageName,
workflowId: parameters.workflowId,
planId: body.planId,
runId: body.runId,
stepRunIds: body.stepRunIds,
principal: authorized.principal,
policyFence: authorized.policyFence,
plannedAtMs,
});
return response(result.status === 'created' ? 201 : 200, {
schema: CLUSTER_PLUGIN_PACKAGE_WORKFLOW_START_RESPONSE_SCHEMA,
status: result.status,
replayed: result.status === 'existing',
planId: result.plan.planId,
runId: result.plan.runId,
receiptDigest: result.receipt.receiptDigest,
});
} catch (error) {
return errorResponse(error);
}
},
}),
Object.freeze({
...common,
method: 'POST' as const,
path: '/api/v3/projects/{projectId}/packages/{packageName}/workflows/{workflowId}/runs/{runId}/cancellation',
operationId: 'workflow.cancel',
permission: 'run.stop',
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
let body;
try {
body = parseClusterRunCancellationRequestBody(
authorized.request.body,
);
} catch {
return response(400, {
code: 'invalid_workflow_cancellation_request',
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
});
}
if (
authorized.projectId === null ||
typeof parameters.packageName !== 'string' ||
!PACKAGE_NAME.test(parameters.packageName) ||
typeof parameters.workflowId !== 'string' ||
!RESOURCE_ID.test(parameters.workflowId) ||
typeof parameters.runId !== 'string' ||
!UUID_V4.test(parameters.runId) ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null
) {
return response(503, {
code: 'workflow_administration_unavailable',
});
}
try {
const result = await capability.cancel({
projectId: authorized.projectId,
packageName: parameters.packageName,
workflowId: parameters.workflowId,
runId: parameters.runId,
mutationId: body.mutationId,
eventId: createEventId(),
principal: authorized.principal,
policyFence: authorized.policyFence,
});
return response(
result.status === 'accepted' ? 202 : 200,
createClusterRunCancellationResponseBody(result),
);
} catch (error) {
return errorResponse(error);
}
},
}),
]);
}
@@ -0,0 +1,372 @@
import type {
DeploymentProfile,
OpenPostgresDatabase,
} from '@qinglong/runtime-core';
import {
createPostgresDatabaseOpener,
isPostgresTlsDnsServername,
loadPostgresConnectionEnvironment,
loadPostgresCertificateAuthorityFile,
type PostgresConnectionOptions,
type PostgresPoolOptions,
} from '@qinglong/cluster-postgres/runtime';
import { ClusterControlAvailabilityFence } from '../database/availability';
import type { ClusterControlHttpSurfaceOptions } from '../transport/httpSurface';
export type ClusterControlEnvironment = Readonly<
Record<string, string | undefined>
>;
export interface DisabledClusterControlConfig {
readonly enabled: false;
readonly profile: DeploymentProfile;
}
export interface EnabledClusterControlConfig {
readonly enabled: true;
readonly profile: 'cluster-control';
readonly http: ClusterControlHttpSurfaceOptions;
readonly database: Readonly<{
connection: PostgresConnectionOptions;
pool: PostgresPoolOptions;
}>;
readonly security: Readonly<{
apiCredentialPepper: string;
}>;
}
export type ClusterControlConfig =
| DisabledClusterControlConfig
| EnabledClusterControlConfig;
export interface ClusterControlDatabaseBinding {
readonly availability: ClusterControlAvailabilityFence;
readonly openDatabase: OpenPostgresDatabase;
}
export class ClusterControlConfigError extends TypeError {
constructor(message: string) {
super(`Cluster-control configuration is invalid: ${message}`);
this.name = 'ClusterControlConfigError';
}
}
const PROFILES = new Set<DeploymentProfile>([
'edge',
'standalone',
'cluster-control',
'worker',
]);
function booleanValue(
environment: ClusterControlEnvironment,
name: string,
defaultValue: boolean,
): boolean {
const value = environment[name];
if (value === undefined || value === '') return defaultValue;
if (value === 'true') return true;
if (value === 'false') return false;
throw new ClusterControlConfigError(`${name} must be true or false`);
}
function integerValue(
environment: ClusterControlEnvironment,
name: string,
defaultValue: number,
minimum: number,
maximum: number,
): number {
const value = environment[name];
if (value === undefined || value === '') return defaultValue;
if (!/^\d+$/.test(value)) {
throw new ClusterControlConfigError(`${name} must be an integer`);
}
const parsed = Number(value);
if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) {
throw new ClusterControlConfigError(
`${name} must be between ${minimum} and ${maximum}`,
);
}
return parsed;
}
function boundedValue(
environment: ClusterControlEnvironment,
name: string,
maximumLength: number,
required = false,
): string | undefined {
const value = environment[name];
if (value === undefined || value === '') {
if (required) throw new ClusterControlConfigError(`${name} is required`);
return undefined;
}
if (value.length > maximumLength || /[\0\r\n]/.test(value)) {
throw new ClusterControlConfigError(`${name} is invalid`);
}
return value;
}
function deploymentProfile(
environment: ClusterControlEnvironment,
): DeploymentProfile {
const value = environment.QL_DEPLOYMENT_PROFILE ?? 'standalone';
if (!PROFILES.has(value as DeploymentProfile)) {
throw new ClusterControlConfigError('QL_DEPLOYMENT_PROFILE is invalid');
}
return value as DeploymentProfile;
}
function runtimeConnection(
environment: ClusterControlEnvironment,
): PostgresConnectionOptions {
let connection: PostgresConnectionOptions;
try {
connection = loadPostgresConnectionEnvironment(environment, {
connectionString: 'QL3_POSTGRES_RUNTIME_URL',
host: 'QL3_POSTGRES_RUNTIME_HOST',
port: 'QL3_POSTGRES_RUNTIME_PORT',
database: 'QL3_POSTGRES_RUNTIME_DATABASE',
user: 'QL3_POSTGRES_RUNTIME_USER',
password: 'QL3_POSTGRES_RUNTIME_PASSWORD',
});
} catch (error) {
throw new ClusterControlConfigError(
error instanceof Error
? error.message
: 'PostgreSQL runtime connection is invalid',
);
}
const mode = environment.QL3_POSTGRES_TLS_MODE ?? 'verify-full';
if (mode !== 'verify-full' && mode !== 'disable') {
throw new ClusterControlConfigError(
'QL3_POSTGRES_TLS_MODE must be verify-full or disable',
);
}
if (
mode === 'disable' &&
!booleanValue(environment, 'QL3_POSTGRES_ALLOW_INSECURE', false)
) {
throw new ClusterControlConfigError(
'disabling PostgreSQL TLS requires QL3_POSTGRES_ALLOW_INSECURE=true',
);
}
const servername = boundedValue(
environment,
'QL3_POSTGRES_TLS_SERVERNAME',
253,
);
if (mode === 'verify-full' && !isPostgresTlsDnsServername(servername)) {
throw new ClusterControlConfigError(
'QL3_POSTGRES_TLS_SERVERNAME must be an explicit DNS name for verify-full',
);
}
const certificateAuthorityFile = boundedValue(
environment,
'QL3_POSTGRES_TLS_CA_FILE',
4096,
);
if (mode === 'disable' && certificateAuthorityFile !== undefined) {
throw new ClusterControlConfigError(
'QL3_POSTGRES_TLS_CA_FILE cannot be used when TLS is disabled',
);
}
let certificateAuthority: string | undefined;
if (certificateAuthorityFile !== undefined) {
try {
certificateAuthority = loadPostgresCertificateAuthorityFile(
certificateAuthorityFile,
);
} catch {
throw new ClusterControlConfigError(
'QL3_POSTGRES_TLS_CA_FILE must contain a bounded trusted CA bundle',
);
}
}
return Object.freeze({
...connection,
tls:
mode === 'disable'
? Object.freeze({ mode: 'disable' as const })
: Object.freeze({
mode: 'verify-full' as const,
...(certificateAuthority === undefined
? {}
: { ca: certificateAuthority }),
servername: servername!,
}),
});
}
function apiCredentialPepper(environment: ClusterControlEnvironment): string {
const value = boundedValue(
environment,
'QL3_API_CREDENTIAL_PEPPER',
64,
true,
)!;
if (!/^[A-Za-z0-9_-]{43}$/.test(value)) {
throw new ClusterControlConfigError(
'QL3_API_CREDENTIAL_PEPPER must be canonical base64url for 32 bytes',
);
}
const decoded = Buffer.from(value, 'base64url');
if (decoded.byteLength !== 32 || decoded.toString('base64url') !== value) {
throw new ClusterControlConfigError(
'QL3_API_CREDENTIAL_PEPPER must be canonical base64url for 32 bytes',
);
}
decoded.fill(0);
return value;
}
/**
* Parses the profile gate before reading PostgreSQL configuration. A disabled
* cluster-control therefore does not touch its runtime credential source.
*/
export function loadClusterControlConfig(
environment: ClusterControlEnvironment,
): ClusterControlConfig {
if (
!environment ||
typeof environment !== 'object' ||
Array.isArray(environment)
) {
throw new ClusterControlConfigError('environment must be an object');
}
const profile = deploymentProfile(environment);
const enabled = booleanValue(
environment,
'QL3_CLUSTER_CONTROL_ENABLED',
false,
);
if (!enabled) return Object.freeze({ enabled: false, profile });
if (profile !== 'cluster-control') {
throw new ClusterControlConfigError(
'enabled runtime requires QL_DEPLOYMENT_PROFILE=cluster-control',
);
}
const applicationName =
boundedValue(environment, 'QL3_POSTGRES_APPLICATION_NAME', 63) ??
'qinglong-cluster-runtime';
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,62}$/.test(applicationName)) {
throw new ClusterControlConfigError(
'QL3_POSTGRES_APPLICATION_NAME is invalid',
);
}
const host =
boundedValue(environment, 'QL3_CLUSTER_HTTP_HOST', 253) ?? '0.0.0.0';
const config: EnabledClusterControlConfig = {
enabled: true,
profile: 'cluster-control',
http: Object.freeze({
host,
port: integerValue(environment, 'QL3_CLUSTER_HTTP_PORT', 5800, 1, 65_535),
maxBodyBytes: integerValue(
environment,
'QL3_CLUSTER_HTTP_MAX_BODY_BYTES',
1024 * 1024,
1024,
4 * 1024 * 1024,
),
maxInFlightRequests: integerValue(
environment,
'QL3_CLUSTER_HTTP_MAX_IN_FLIGHT',
64,
1,
1024,
),
authenticationRateWindowMs: integerValue(
environment,
'QL3_CLUSTER_AUTH_RATE_WINDOW_MS',
60_000,
1_000,
60 * 60_000,
),
authenticationRatePerPeer: integerValue(
environment,
'QL3_CLUSTER_AUTH_RATE_PER_PEER',
300,
1,
1_000_000,
),
authenticationRateGlobal: integerValue(
environment,
'QL3_CLUSTER_AUTH_RATE_GLOBAL',
1_200,
1,
1_000_000,
),
authenticationRateMaxPeers: integerValue(
environment,
'QL3_CLUSTER_AUTH_RATE_MAX_PEERS',
4_096,
1,
65_536,
),
requestTimeoutMs: integerValue(
environment,
'QL3_CLUSTER_HTTP_REQUEST_TIMEOUT_MS',
15_000,
100,
120_000,
),
drainTimeoutMs: integerValue(
environment,
'QL3_CLUSTER_HTTP_DRAIN_TIMEOUT_MS',
10_000,
100,
120_000,
),
}),
database: Object.freeze({
connection: runtimeConnection(environment),
pool: Object.freeze({
applicationName,
maxConnections: integerValue(
environment,
'QL3_POSTGRES_MAX_CONNECTIONS',
8,
1,
64,
),
connectionTimeoutMs: integerValue(
environment,
'QL3_POSTGRES_CONNECTION_TIMEOUT_MS',
5_000,
100,
60_000,
),
}),
}),
security: Object.freeze({
apiCredentialPepper: apiCredentialPepper(environment),
}),
};
return Object.freeze(config);
}
export function createClusterControlDatabaseBinding(
config: EnabledClusterControlConfig,
): ClusterControlDatabaseBinding {
if (!config?.enabled || config.profile !== 'cluster-control') {
throw new ClusterControlConfigError(
'database binding requires an enabled cluster-control config',
);
}
const availability = new ClusterControlAvailabilityFence();
const openDatabase = createPostgresDatabaseOpener({
role: 'runtime',
connection: config.database.connection,
pool: config.database.pool,
onPoolError(error) {
// pg emits idle-client errors outside a request Promise. They are an
// availability signal, never a callback exception or transaction retry.
void availability.signal(error).catch(() => undefined);
},
});
return Object.freeze({ availability, openDatabase });
}
@@ -0,0 +1,416 @@
import type {
ClusterControlActivationAudit,
ClusterControlStopResult,
} from '@qinglong/runtime-core';
import {
loadClusterControlConfig,
type ClusterControlEnvironment,
type EnabledClusterControlConfig,
} from './config';
import {
startProductionClusterControlApplication,
type ProductionClusterControlApplicationOptions,
} from '../application-runtime/productionApplication';
import {
ClusterControlDatabaseUnavailableError,
type ClusterControlApplicationResult,
} from '../application-runtime/application';
import {
loadClusterWorkerIngressConfig,
type EnabledClusterWorkerIngressConfig,
} from '../worker-ingress/workerIngressConfig';
import type { ClusterWorkerArtifactBinding } from '../artifact/workerArtifactBinding';
import type { RemoteWorkerSecretValueProvider } from '@qinglong/runtime-core/remote-secret-delivery';
export type ClusterControlProcessSignal = 'SIGINT' | 'SIGTERM';
export interface ClusterControlProcessEvent {
readonly schemaVersion: 1;
readonly component: 'qinglong3-cluster-control';
readonly level: 'info' | 'error';
readonly event: string;
readonly replicaId: string;
readonly signal?: ClusterControlProcessSignal;
readonly stopResult?: ClusterControlStopResult;
readonly address?: Readonly<{ host: string; port: number }>;
readonly activation?: ClusterControlActivationAudit;
readonly diagnostic?: Readonly<{
scope:
| 'scheduler'
| 'cancellation-convergence'
| 'database'
| 'worker-ingress';
name: string;
code?: string;
}>;
}
export interface ClusterControlProcessSignalSource {
subscribe(
listener: (signal: ClusterControlProcessSignal) => void,
): () => void;
}
export type ProductionClusterControlStarter = (
options: ProductionClusterControlApplicationOptions,
) => Promise<ClusterControlApplicationResult>;
export type ClusterWorkerArtifactBindingFactory = (
config: EnabledClusterWorkerIngressConfig['artifact'],
) => Promise<Readonly<ClusterWorkerArtifactBinding>>;
export type ClusterWorkerSecretProviderFactory = (
config: NonNullable<EnabledClusterWorkerIngressConfig['secret']>,
) => Promise<Readonly<RemoteWorkerSecretValueProvider>>;
export interface ProductionClusterControlProcessOptions {
readonly environment: ClusterControlEnvironment;
readonly signals: ClusterControlProcessSignalSource;
readonly emit: (event: ClusterControlProcessEvent) => void | Promise<void>;
readonly start?: ProductionClusterControlStarter;
readonly createWorkerArtifactBinding?: ClusterWorkerArtifactBindingFactory;
readonly createWorkerSecretProvider?: ClusterWorkerSecretProviderFactory;
readonly workerSecretProvider?: RemoteWorkerSecretValueProvider;
}
export class ClusterControlProcessError extends Error {
readonly code:
| 'QL3_CLUSTER_CONTROL_PROCESS_CONFIG_INVALID'
| 'QL3_CLUSTER_CONTROL_PROCESS_DISABLED';
constructor(
code: ClusterControlProcessError['code'],
message: string,
) {
super(message);
this.name = 'ClusterControlProcessError';
this.code = code;
}
}
const REPLICA_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
function processConfiguration(environment: ClusterControlEnvironment): {
readonly config: EnabledClusterControlConfig;
readonly workerIngress?: EnabledClusterWorkerIngressConfig;
readonly replicaId: string;
} {
const config = loadClusterControlConfig(environment);
if (!config.enabled) {
throw new ClusterControlProcessError(
'QL3_CLUSTER_CONTROL_PROCESS_DISABLED',
'The cluster-control process requires an enabled cluster-control profile',
);
}
const replicaId = environment.QL3_CLUSTER_REPLICA_ID;
if (
typeof replicaId !== 'string' ||
!REPLICA_ID_PATTERN.test(replicaId)
) {
throw new ClusterControlProcessError(
'QL3_CLUSTER_CONTROL_PROCESS_CONFIG_INVALID',
'QL3_CLUSTER_REPLICA_ID must be a stable safe identifier',
);
}
const workerIngress = loadClusterWorkerIngressConfig(environment);
return Object.freeze({
config,
replicaId,
...(workerIngress.enabled ? { workerIngress } : {}),
});
}
async function createWorkerArtifactBinding(
config: EnabledClusterWorkerIngressConfig['artifact'],
): Promise<Readonly<ClusterWorkerArtifactBinding>> {
const binding = await import('../artifact/workerArtifactBinding.js');
return binding.createClusterWorkerArtifactBinding(config);
}
async function createWorkerSecretProvider(
config: NonNullable<EnabledClusterWorkerIngressConfig['secret']>,
): Promise<Readonly<RemoteWorkerSecretValueProvider>> {
if (config.provider !== 'mounted-files') {
throw new TypeError('Cluster Worker Secret provider is unsupported');
}
const provider = await import('../remote-execution/mountedSecretProvider.js');
return provider.createClusterMountedSecretProvider({
rootDirectory: config.rootDirectory,
});
}
function diagnosticFact(
scope: ClusterControlProcessEvent['diagnostic'] extends infer T
? T extends { readonly scope: infer TScope }
? TScope
: never
: never,
error: unknown,
): NonNullable<ClusterControlProcessEvent['diagnostic']> {
const candidate = error as {
readonly name?: unknown;
readonly code?: unknown;
};
return Object.freeze({
scope,
name:
typeof candidate?.name === 'string' && candidate.name.length <= 128
? candidate.name
: 'Error',
...(typeof candidate?.code === 'string' && candidate.code.length <= 128
? { code: candidate.code }
: {}),
});
}
function event(
replicaId: string,
values: Omit<
ClusterControlProcessEvent,
'schemaVersion' | 'component' | 'replicaId'
>,
): ClusterControlProcessEvent {
return Object.freeze({
schemaVersion: 1,
component: 'qinglong3-cluster-control',
replicaId,
...values,
});
}
/**
* Owns exactly one production cluster-control process. It installs signal
* handling before startup, derives every lease owner from the stable replica
* identity, and withdraws admission through the application stop contract.
*/
export async function runProductionClusterControlProcess(
options: ProductionClusterControlProcessOptions,
): Promise<ClusterControlStopResult> {
if (
!options ||
typeof options !== 'object' ||
typeof options.emit !== 'function' ||
typeof options.signals?.subscribe !== 'function'
) {
throw new TypeError('Cluster-control process options are invalid');
}
const { config, replicaId, workerIngress } = processConfiguration(
options.environment,
);
const start = options.start ?? startProductionClusterControlApplication;
if (typeof start !== 'function') {
throw new TypeError('Cluster-control process starter is invalid');
}
let resolveSignal:
| ((signal: ClusterControlProcessSignal) => void)
| undefined;
const requestedSignal = new Promise<ClusterControlProcessSignal>((resolve) => {
resolveSignal = resolve;
});
let acceptedSignal = false;
const unsubscribe = options.signals.subscribe((signal) => {
if (acceptedSignal) return;
acceptedSignal = true;
resolveSignal?.(signal);
});
let artifactBinding: Readonly<ClusterWorkerArtifactBinding> | undefined;
let workerSecretProvider = options.workerSecretProvider;
let application: ClusterControlApplicationResult | undefined;
let applicationStopStarted = false;
let primaryError: unknown;
try {
if (workerIngress) {
const createBinding =
options.createWorkerArtifactBinding ?? createWorkerArtifactBinding;
if (typeof createBinding !== 'function') {
throw new TypeError(
'Cluster Worker Artifact binding factory is invalid',
);
}
artifactBinding = await createBinding(workerIngress.artifact);
if (
workerIngress.secret !== undefined &&
workerSecretProvider === undefined
) {
const createProvider =
options.createWorkerSecretProvider ?? createWorkerSecretProvider;
if (typeof createProvider !== 'function') {
throw new TypeError(
'Cluster Worker Secret provider factory is invalid',
);
}
workerSecretProvider = await createProvider(workerIngress.secret);
}
if (
workerSecretProvider !== undefined &&
typeof workerSecretProvider.resolve !== 'function'
) {
throw new TypeError('Cluster Worker Secret provider is invalid');
}
}
application = await start({
config,
recovery: { ownerId: replicaId },
scheduler: {
ownerId: replicaId,
onDiagnostic(error) {
void Promise.resolve(
options.emit(
event(replicaId, {
level: 'error',
event: 'runtime_diagnostic',
diagnostic: diagnosticFact('scheduler', error),
}),
),
).catch(() => undefined);
},
},
cancellationConvergence: {
onDiagnostic(error) {
void Promise.resolve(
options.emit(
event(replicaId, {
level: 'error',
event: 'runtime_diagnostic',
diagnostic: diagnosticFact(
'cancellation-convergence',
error,
),
}),
),
).catch(() => undefined);
},
},
...(workerIngress === undefined
? {}
: {
workerIngress: {
config: workerIngress,
artifactStore: artifactBinding!.store,
...(workerSecretProvider === undefined
? {}
: { secretProvider: workerSecretProvider }),
onDiagnostic(error: unknown) {
void Promise.resolve(
options.emit(
event(replicaId, {
level: 'error',
event: 'runtime_diagnostic',
diagnostic: diagnosticFact(
'worker-ingress',
error,
),
}),
),
).catch(() => undefined);
},
},
}),
audit(record) {
return options.emit(
event(replicaId, {
level: record.state === 'failed' ? 'error' : 'info',
event: 'activation',
activation: Object.freeze({ ...record }),
}),
);
},
});
if (application.status !== 'active') {
throw new ClusterControlProcessError(
'QL3_CLUSTER_CONTROL_PROCESS_DISABLED',
'The cluster-control process did not activate',
);
}
await options.emit(
event(replicaId, {
level: 'info',
event: 'listening',
address: application.address,
}),
);
if (workerIngress) {
await options.emit(
event(replicaId, {
level: 'info',
event: 'worker_ingress_listening',
address: Object.freeze({
host: workerIngress.http.host ?? '0.0.0.0',
port: workerIngress.http.port ?? 5801,
}),
}),
);
}
const termination = await Promise.race([
requestedSignal.then((signal) =>
Object.freeze({ kind: 'signal' as const, signal }),
),
application.unavailable.then((error) =>
Object.freeze({ kind: 'database-unavailable' as const, error }),
),
]);
if (termination.kind === 'database-unavailable') {
await options.emit(
event(replicaId, {
level: 'error',
event: 'database_unavailable',
diagnostic: diagnosticFact('database', termination.error),
}),
);
applicationStopStarted = true;
const stopResult = await application.stop();
await options.emit(
event(replicaId, {
level: stopResult === 'stopped' ? 'info' : 'error',
event: 'stopped',
stopResult,
}),
);
throw new ClusterControlDatabaseUnavailableError();
}
const signal = termination.signal;
await options.emit(
event(replicaId, {
level: 'info',
event: 'shutdown_requested',
signal,
}),
);
applicationStopStarted = true;
const stopResult = await application.stop();
await options.emit(
event(replicaId, {
level: stopResult === 'stopped' ? 'info' : 'error',
event: 'stopped',
stopResult,
}),
);
return stopResult;
} catch (error) {
primaryError = error;
throw error;
} finally {
unsubscribe();
resolveSignal = undefined;
let cleanupError: unknown;
if (
application?.status === 'active' &&
!applicationStopStarted
) {
try {
applicationStopStarted = true;
await application.stop();
} catch (error) {
cleanupError = error;
}
}
try {
await artifactBinding?.close();
} catch (error) {
cleanupError ??= error;
}
if (cleanupError && primaryError === undefined) throw cleanupError;
}
}
@@ -0,0 +1,260 @@
// Remote Execution owns mounted Secret resolution for authenticated delivery.
import { createHash } from 'node:crypto';
import { constants } from 'node:fs';
import {
lstat,
open,
realpath,
} from 'node:fs/promises';
import {
isAbsolute,
join,
normalize,
parse,
relative,
} from 'node:path';
import {
MAX_REMOTE_SECRET_DELIVERY_TOTAL_VALUE_BYTES,
MAX_REMOTE_SECRET_VALUE_BYTES,
normalizeRemoteWorkerSecretDeliveryAuthority,
type RemoteWorkerSecretDeliveryAuthority,
type RemoteWorkerSecretResolution,
type RemoteWorkerSecretValueProvider,
} from '@qinglong/runtime-core/remote-secret-delivery';
import { parseSecretRef } from '@qinglong/runtime-core/secret-reference';
const MAX_SECRET_ROOT_BYTES = 4096;
const SECRET_FILE_NAME = /^[0-9a-f]{64}$/;
export interface ClusterMountedSecretProviderOptions {
/**
* Read-only directory whose file names are SHA-256(canonical SecretRef).
* Kubernetes projected-volume symlinks are accepted only when their resolved
* regular file remains below this directory.
*/
readonly rootDirectory: string;
}
export class ClusterMountedSecretProviderError extends Error {
readonly code = 'QL3_CLUSTER_MOUNTED_SECRET_UNAVAILABLE';
constructor(
readonly reason:
| 'invalid_configuration'
| 'root_unavailable'
| 'material_unavailable',
options?: ErrorOptions,
) {
super(`Cluster mounted Secret provider failed: ${reason}`, options);
this.name = 'ClusterMountedSecretProviderError';
}
}
function rootDirectory(value: string): string {
if (
typeof value !== 'string' ||
!isAbsolute(value) ||
parse(value).root === value ||
normalize(value) !== value ||
value.includes('\0') ||
Buffer.byteLength(value, 'utf8') > MAX_SECRET_ROOT_BYTES
) {
throw new ClusterMountedSecretProviderError('invalid_configuration');
}
return value;
}
/**
* Kubernetes Secret keys cannot contain a SecretRef directly. This stable,
* non-reversible name also prevents Project/name input from becoming a path.
*/
export function clusterMountedSecretFileName(secretRef: string): string {
let canonical: string;
try {
const parsed = parseSecretRef(secretRef);
canonical = secretRef;
if (
parsed.projectId.length < 1 ||
parsed.name.length < 1
) throw new Error('invalid SecretRef');
} catch (error) {
throw new ClusterMountedSecretProviderError(
'invalid_configuration',
{ cause: error },
);
}
const name = createHash('sha256').update(canonical, 'utf8').digest('hex');
if (!SECRET_FILE_NAME.test(name)) {
throw new ClusterMountedSecretProviderError('invalid_configuration');
}
return name;
}
function remainsBelow(root: string, candidate: string): boolean {
const suffix = relative(root, candidate);
return (
suffix.length > 0 &&
!isAbsolute(suffix) &&
suffix !== '..' &&
!suffix.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`)
);
}
async function resolvedRoot(path: string): Promise<string> {
try {
const configured = await lstat(path);
if (!configured.isDirectory() || configured.isSymbolicLink()) {
throw new Error('root is not a direct directory');
}
return await realpath(path);
} catch (error) {
throw new ClusterMountedSecretProviderError(
'root_unavailable',
{ cause: error },
);
}
}
async function readMaterial(
root: string,
secretRef: string,
): Promise<Buffer> {
const candidate = join(root, clusterMountedSecretFileName(secretRef));
let handle;
try {
const target = await realpath(candidate);
if (!remainsBelow(root, target)) {
throw new Error('material escaped its root');
}
handle = await open(
target,
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
);
const stat = await handle.stat();
if (
!stat.isFile() ||
stat.nlink !== 1 ||
stat.size < 0 ||
stat.size > MAX_REMOTE_SECRET_VALUE_BYTES ||
(stat.mode & 0o111) !== 0 ||
(stat.mode & 0o027) !== 0
) {
throw new Error('material metadata is unsafe');
}
const bytes = await handle.readFile();
if (
bytes.byteLength !== stat.size ||
bytes.byteLength > MAX_REMOTE_SECRET_VALUE_BYTES ||
(await realpath(candidate)) !== target
) {
bytes.fill(0);
throw new Error('material changed while reading');
}
return bytes;
} catch (error) {
throw new ClusterMountedSecretProviderError(
'material_unavailable',
{ cause: error },
);
} finally {
await handle?.close().catch(() => undefined);
}
}
function secretValue(bytes: Buffer): string {
try {
const value = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
if (value.includes('\0')) {
throw new Error('Secret contains NUL');
}
return value;
} catch (error) {
throw new ClusterMountedSecretProviderError(
'material_unavailable',
{ cause: error },
);
}
}
/**
* A zero-client, zero-watcher Cluster provider for Kubernetes Secret, CSI or
* operator-managed projected files. Every authorized delivery resolves the
* active files again, so atomic projection replacement rotates material
* without a timer, cache, control restart or Kubernetes API permission.
*/
export class ClusterMountedSecretProvider
implements RemoteWorkerSecretValueProvider
{
private readonly rootDirectory: string;
constructor(options: ClusterMountedSecretProviderOptions) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new ClusterMountedSecretProviderError('invalid_configuration');
}
this.rootDirectory = rootDirectory(options.rootDirectory);
}
async verify(): Promise<void> {
await resolvedRoot(this.rootDirectory);
}
async resolve(
authority: Readonly<RemoteWorkerSecretDeliveryAuthority>,
): Promise<Readonly<RemoteWorkerSecretResolution>> {
let normalized: Readonly<RemoteWorkerSecretDeliveryAuthority>;
try {
normalized = normalizeRemoteWorkerSecretDeliveryAuthority(authority);
} catch (error) {
throw new ClusterMountedSecretProviderError(
'material_unavailable',
{ cause: error },
);
}
const root = await resolvedRoot(this.rootDirectory);
const buffers: Buffer[] = [];
try {
const values = [];
let totalBytes = 0;
for (const secretRef of normalized.secretRefs) {
const bytes = await readMaterial(root, secretRef);
buffers.push(bytes);
totalBytes += bytes.byteLength;
if (totalBytes > MAX_REMOTE_SECRET_DELIVERY_TOTAL_VALUE_BYTES) {
throw new ClusterMountedSecretProviderError(
'material_unavailable',
);
}
values.push(
Object.freeze({
secretRef,
value: secretValue(bytes),
}),
);
}
let disposed = false;
return Object.freeze({
values: Object.freeze(values),
dispose() {
if (disposed) return;
disposed = true;
for (const bytes of buffers) bytes.fill(0);
},
});
} catch (error) {
for (const bytes of buffers) bytes.fill(0);
if (error instanceof ClusterMountedSecretProviderError) throw error;
throw new ClusterMountedSecretProviderError(
'material_unavailable',
{ cause: error },
);
}
}
}
export async function createClusterMountedSecretProvider(
options: ClusterMountedSecretProviderOptions,
): Promise<Readonly<ClusterMountedSecretProvider>> {
const provider = new ClusterMountedSecretProvider(options);
await provider.verify();
return provider;
}
@@ -0,0 +1,114 @@
// Remote execution owns Worker-bound activation acknowledgements and start failure fencing.
import { randomUUID } from 'node:crypto';
import type {
AcknowledgeRemoteRunRunningCommand,
AcknowledgeRemoteRunStartingCommand,
FailRemoteRunStartCommand,
RemoteRunActivationRepository,
RemoteRunActivationResult,
} from '@qinglong/runtime-core/remote-activation';
export interface ClusterRemoteRunActivationPrincipal {
readonly workerId: string;
}
type ServerOwnedStartingFields = 'workerId' | 'eventId';
type ServerOwnedRunningFields = 'workerId' | 'attemptEventId' | 'runEventId';
export type AcknowledgeClusterRemoteRunStartingCommand = Omit<
AcknowledgeRemoteRunStartingCommand,
ServerOwnedStartingFields
>;
export type AcknowledgeClusterRemoteRunRunningCommand = Omit<
AcknowledgeRemoteRunRunningCommand,
ServerOwnedRunningFields
>;
export type FailClusterRemoteRunStartCommand = Omit<
FailRemoteRunStartCommand,
ServerOwnedRunningFields
>;
export interface ClusterRemoteRunActivationServiceOptions {
readonly createEventId?: () => string;
}
export class ClusterRemoteRunActivationService {
private readonly createEventId: () => string;
constructor(
private readonly repository: RemoteRunActivationRepository,
options: ClusterRemoteRunActivationServiceOptions = {},
) {
if (
!repository ||
typeof repository.acknowledgeStarting !== 'function' ||
typeof repository.acknowledgeRunning !== 'function' ||
typeof repository.failStart !== 'function'
) {
throw new TypeError('Remote Run activation repository is invalid');
}
if (
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
Object.keys(options).some((key) => key !== 'createEventId')
) {
throw new TypeError('Remote Run activation service options are invalid');
}
this.createEventId = options.createEventId ?? randomUUID;
if (typeof this.createEventId !== 'function') {
throw new TypeError('Remote Run activation event ID factory is invalid');
}
}
acknowledgeStarting(
principal: ClusterRemoteRunActivationPrincipal,
command: AcknowledgeClusterRemoteRunStartingCommand,
): Promise<Readonly<RemoteRunActivationResult>> {
this.assertPrincipal(principal);
return this.repository.acknowledgeStarting({
...command,
workerId: principal.workerId,
eventId: this.createEventId(),
});
}
acknowledgeRunning(
principal: ClusterRemoteRunActivationPrincipal,
command: AcknowledgeClusterRemoteRunRunningCommand,
): Promise<Readonly<RemoteRunActivationResult>> {
this.assertPrincipal(principal);
return this.repository.acknowledgeRunning({
...command,
workerId: principal.workerId,
attemptEventId: this.createEventId(),
runEventId: this.createEventId(),
});
}
failStart(
principal: ClusterRemoteRunActivationPrincipal,
command: FailClusterRemoteRunStartCommand,
): Promise<Readonly<RemoteRunActivationResult>> {
this.assertPrincipal(principal);
return this.repository.failStart({
...command,
workerId: principal.workerId,
attemptEventId: this.createEventId(),
runEventId: this.createEventId(),
});
}
private assertPrincipal(principal: ClusterRemoteRunActivationPrincipal): void {
if (
!principal ||
typeof principal !== 'object' ||
Array.isArray(principal) ||
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(principal.workerId)
) {
throw new TypeError('Remote Run activation principal is invalid');
}
}
}
@@ -0,0 +1,380 @@
// Remote execution owns immutable Artifact admission and fenced Worker completion.
import { randomUUID } from 'node:crypto';
import {
InvalidRemoteWorkerCompletionError,
MAX_REMOTE_WORKER_ARTIFACT_HEADER_BYTES,
RemoteWorkerCompletionFenceRejectedError,
RemoteWorkerCompletionUnavailableError,
normalizeRemoteWorkerArtifactReceipt,
normalizeRemoteWorkerCompletionCommand,
normalizeRemoteWorkerCompletionResult,
parseRemoteWorkerArtifactUploadHeader,
type RemoteWorkerArtifactReceipt,
type RemoteWorkerArtifactUploadAuthorityRepository,
type RemoteWorkerArtifactUploadCommand,
type RemoteWorkerCompletionCommand,
type RemoteWorkerCompletionRepository,
type RemoteWorkerCompletionResult,
} from '@qinglong/runtime-core/remote-worker-completion';
export interface ClusterRemoteWorkerArtifactStorageCommand {
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly logArtifactId: string;
readonly byteLength: number;
readonly truncated?: boolean;
}
export interface ClusterRemoteWorkerArtifactLookup {
readonly projectId: string;
readonly runId: string;
readonly attemptId: string;
readonly logArtifactId: string;
}
/**
* Production implementations must be shared by every cluster-control replica
* and provide immutable, digest-authenticated put-if-absent semantics.
*/
export interface ClusterRemoteWorkerArtifactStore {
put(
command: Readonly<ClusterRemoteWorkerArtifactStorageCommand>,
content: AsyncIterable<Uint8Array>,
signal?: AbortSignal,
): Promise<Readonly<RemoteWorkerArtifactReceipt>>;
inspect(
lookup: Readonly<ClusterRemoteWorkerArtifactLookup>,
signal?: AbortSignal,
): Promise<Readonly<RemoteWorkerArtifactReceipt> | undefined>;
}
export interface ClusterRemoteWorkerArtifactUploadInput {
readonly workerId: string;
readonly workerSessionId: string;
readonly contentLength: number;
readonly chunks: AsyncIterable<Uint8Array>;
readonly signal?: AbortSignal;
}
export interface ClusterRemoteWorkerCompletionServiceOptions {
readonly createEventId?: () => string;
}
class BoundedArtifactStreamReader {
private readonly iterator: AsyncIterator<Uint8Array>;
private pending: Uint8Array | undefined;
private pendingOffset = 0;
constructor(
source: AsyncIterable<Uint8Array>,
private readonly signal?: AbortSignal,
) {
if (!source || typeof source[Symbol.asyncIterator] !== 'function') {
throw new InvalidRemoteWorkerCompletionError(
'Artifact upload stream is invalid',
);
}
this.iterator = source[Symbol.asyncIterator]();
}
async readExactly(byteLength: number): Promise<Buffer> {
const result = Buffer.allocUnsafe(byteLength);
let written = 0;
try {
while (written < byteLength) {
const chunk = await this.nextChunk();
if (!chunk) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact upload stream ended before its header',
);
}
const available = chunk.byteLength - this.pendingOffset;
const copied = Math.min(available, byteLength - written);
Buffer.from(
chunk.buffer,
chunk.byteOffset + this.pendingOffset,
copied,
).copy(result, written);
written += copied;
this.pendingOffset += copied;
if (this.pendingOffset === chunk.byteLength) {
this.pending = undefined;
this.pendingOffset = 0;
}
}
return result;
} catch (error) {
result.fill(0);
throw error;
}
}
content(byteLength: number): Readonly<{
chunks: AsyncIterable<Uint8Array>;
isComplete(): boolean;
}> {
let complete = false;
let started = false;
const self = this;
const chunks = Object.freeze({
async *[Symbol.asyncIterator](): AsyncGenerator<Uint8Array> {
if (started) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact content can only be consumed once',
);
}
started = true;
let total = 0;
while (true) {
const chunk = await self.nextChunk();
if (!chunk) break;
const bytes = chunk.subarray(self.pendingOffset);
self.pending = undefined;
self.pendingOffset = 0;
total += bytes.byteLength;
if (total > byteLength) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact content exceeds its declared length',
);
}
yield bytes;
}
if (total !== byteLength) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact content does not match its declared length',
);
}
complete = true;
},
});
return Object.freeze({ chunks, isComplete: () => complete });
}
private async nextChunk(): Promise<Uint8Array | undefined> {
if (this.signal?.aborted) throw this.signal.reason;
if (this.pending) return this.pending;
const next = await this.iterator.next();
if (next.done) return undefined;
if (!(next.value instanceof Uint8Array) || next.value.byteLength === 0) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact upload chunk is invalid',
);
}
this.pending = next.value;
this.pendingOffset = 0;
return this.pending;
}
}
function storageCommand(
command: RemoteWorkerArtifactUploadCommand,
): Readonly<ClusterRemoteWorkerArtifactStorageCommand> {
return Object.freeze({
projectId: command.projectId,
runId: command.runId,
attemptId: command.attemptId,
logArtifactId: command.logArtifactId,
byteLength: command.byteLength,
...(command.truncated === undefined
? {}
: { truncated: command.truncated }),
});
}
function assertReceiptMatches(
command: ClusterRemoteWorkerArtifactStorageCommand,
value: RemoteWorkerArtifactReceipt,
): Readonly<RemoteWorkerArtifactReceipt> {
const receipt = normalizeRemoteWorkerArtifactReceipt(value);
if (
receipt.projectId !== command.projectId ||
receipt.runId !== command.runId ||
receipt.attemptId !== command.attemptId ||
receipt.logArtifactId !== command.logArtifactId ||
receipt.byteLength !== command.byteLength ||
receipt.truncated !== command.truncated
) {
throw new RemoteWorkerCompletionUnavailableError();
}
return receipt;
}
function eventId(factory: () => string): string {
const value = factory();
if (
typeof value !== 'string' ||
value.length < 1 ||
value.length > 36 ||
/[\u0000-\u001f\u007f]/.test(value)
) {
throw new RemoteWorkerCompletionUnavailableError();
}
return value;
}
export class ClusterRemoteWorkerArtifactService {
constructor(
private readonly authority: RemoteWorkerArtifactUploadAuthorityRepository,
private readonly store: ClusterRemoteWorkerArtifactStore,
) {
if (
typeof authority?.authorizeArtifactUpload !== 'function' ||
typeof store?.put !== 'function' ||
typeof store?.inspect !== 'function'
) {
throw new TypeError('Remote Worker Artifact service is invalid');
}
}
async upload(
input: ClusterRemoteWorkerArtifactUploadInput,
): Promise<Readonly<RemoteWorkerArtifactReceipt>> {
if (
!input ||
!Number.isSafeInteger(input.contentLength) ||
input.contentLength < 6
) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact upload envelope is invalid',
);
}
const reader = new BoundedArtifactStreamReader(input.chunks, input.signal);
const prefix = await reader.readExactly(4);
const headerLength = prefix.readUInt32BE(0);
prefix.fill(0);
if (
headerLength < 2 ||
headerLength > MAX_REMOTE_WORKER_ARTIFACT_HEADER_BYTES
) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact upload header length is invalid',
);
}
const header = await reader.readExactly(headerLength);
let command: Readonly<RemoteWorkerArtifactUploadCommand>;
try {
command = parseRemoteWorkerArtifactUploadHeader(header, {
workerId: input.workerId,
workerSessionId: input.workerSessionId,
});
} finally {
header.fill(0);
}
if (input.contentLength !== 4 + headerLength + command.byteLength) {
throw new InvalidRemoteWorkerCompletionError(
'Artifact upload envelope length does not match its header',
);
}
try {
await this.authority.authorizeArtifactUpload(command);
const target = storageCommand(command);
const content = reader.content(command.byteLength);
const receipt = await this.store.put(
target,
content.chunks,
input.signal,
);
if (!content.isComplete()) {
throw new Error('Artifact store did not consume the complete body');
}
return assertReceiptMatches(target, receipt);
} catch (error) {
if (
error instanceof InvalidRemoteWorkerCompletionError ||
error instanceof RemoteWorkerCompletionFenceRejectedError ||
error instanceof RemoteWorkerCompletionUnavailableError
) {
throw error;
}
throw new RemoteWorkerCompletionUnavailableError({ cause: error });
}
}
}
export class ClusterRemoteWorkerCompletionService {
private readonly createEventId: () => string;
constructor(
private readonly repository: RemoteWorkerCompletionRepository,
private readonly store: Pick<ClusterRemoteWorkerArtifactStore, 'inspect'>,
options: ClusterRemoteWorkerCompletionServiceOptions = {},
) {
if (
typeof repository?.complete !== 'function' ||
typeof store?.inspect !== 'function' ||
(options.createEventId !== undefined &&
typeof options.createEventId !== 'function')
) {
throw new TypeError('Remote Worker completion service is invalid');
}
this.createEventId = options.createEventId ?? randomUUID;
}
async complete(
value: RemoteWorkerCompletionCommand,
signal?: AbortSignal,
): Promise<Readonly<RemoteWorkerCompletionResult>> {
const command = normalizeRemoteWorkerCompletionCommand(value);
if (signal?.aborted) throw signal.reason;
const lookup = Object.freeze({
projectId: command.projectId,
runId: command.runId,
attemptId: command.attemptId,
logArtifactId: command.artifact.logArtifactId,
});
let stored: Readonly<RemoteWorkerArtifactReceipt> | undefined;
try {
stored = await this.store.inspect(lookup, signal);
} catch (error) {
throw new RemoteWorkerCompletionUnavailableError({ cause: error });
}
if (!stored) {
throw new RemoteWorkerCompletionFenceRejectedError(
command.attemptId,
'state_mismatch',
);
}
const receipt = assertReceiptMatches(
{
...lookup,
byteLength: command.artifact.byteLength,
...(command.artifact.truncated === undefined
? {}
: { truncated: command.artifact.truncated }),
},
stored,
);
if (receipt.sha256 !== command.artifact.sha256) {
throw new RemoteWorkerCompletionFenceRejectedError(
command.attemptId,
'replay_mismatch',
);
}
try {
const result = normalizeRemoteWorkerCompletionResult(
await this.repository.complete(Object.freeze({
...command,
attemptEventId: eventId(this.createEventId),
runEventId: eventId(this.createEventId),
})),
);
if (
result.runId !== command.runId ||
result.attemptId !== command.attemptId ||
result.callbackSequence !== command.callbackSequence
) {
throw new Error('Remote Worker completion authority drifted');
}
return result;
} catch (error) {
if (
error instanceof RemoteWorkerCompletionFenceRejectedError ||
error instanceof RemoteWorkerCompletionUnavailableError
) {
throw error;
}
throw new RemoteWorkerCompletionUnavailableError({ cause: error });
}
}
}
@@ -0,0 +1,380 @@
// Remote execution owns bounded offer selection, placement, and lease claiming.
import { randomBytes, randomUUID } from 'node:crypto';
import type {
ClusterDispatchCandidate,
ClusterDispatchCandidateCursor,
ClusterDispatchSource,
ClusterRemoteExecutionOffer,
} from '@qinglong/runtime-core/remote-dispatch';
import {
assertRemoteDispatchPageSize,
createClusterRemoteExecutionOffer,
evaluateRemoteWorkerPlacement,
leaseTokenMatchesDigest,
normalizeClusterDispatchCandidate,
} from '@qinglong/runtime-core/remote-dispatch';
import type {
ClusterTaskExecutionRevision,
ClusterTaskExecutionRevisionSource,
} from '@qinglong/runtime-core/cluster-execution-revision';
import type {
ClaimRunDispatchLeaseResult,
RunDispatchLeaseRepository,
WorkerSessionRecord,
WorkerSessionRepository,
} from '@qinglong/runtime-core';
import {
assertRunDispatchId,
assertRunDispatchLeaseDuration,
assertRunDispatchLeaseToken,
assertWorkerId,
assertWorkerSessionId,
} from '@qinglong/runtime-core';
import { parseTaskDefinitionRevisionRef } from '@qinglong/runtime-core/task-definition-execution-compiler';
const DEFAULT_PAGE_SIZE = 8;
const DEFAULT_MAX_PAGES = 2;
const DEFAULT_MAX_CLAIMS = 8;
const DEFAULT_LEASE_MS = 30_000;
const MAX_PAGES = 16;
const MAX_CLAIMS = 64;
export interface ClusterRemoteWorkerOfferPrincipal {
readonly workerId: string;
}
export interface ClaimClusterRemoteWorkerOfferCommand {
readonly workerSessionId: string;
readonly workerGeneration: number;
/** Worker-generated stable idempotency key for this poll attempt. */
readonly offerId: string;
/** Worker-generated high-entropy capability; PostgreSQL stores only its digest. */
readonly leaseToken: string;
}
export interface ClusterRemoteWorkerOfferStats {
readonly pages: number;
readonly candidates: number;
readonly plansUnavailable: number;
readonly placementMismatches: number;
readonly claimAttempts: number;
readonly claimRaces: number;
}
type MutableClusterRemoteWorkerOfferStats = {
-readonly [Key in keyof ClusterRemoteWorkerOfferStats]: ClusterRemoteWorkerOfferStats[Key];
};
export type ClaimClusterRemoteWorkerOfferResult =
| Readonly<{
status: 'offered';
offer: ClusterRemoteExecutionOffer;
stats: ClusterRemoteWorkerOfferStats;
truncated: boolean;
}>
| Readonly<{
status: 'idle';
reason:
| 'worker_unavailable'
| 'no_candidates'
| 'no_match'
| 'plans_unavailable'
| 'claim_raced'
| 'claim_budget_exhausted'
| 'scan_budget_exhausted';
stats: ClusterRemoteWorkerOfferStats;
truncated: boolean;
}>;
export class ClusterRemoteWorkerOfferFenceRejectedError extends Error {
readonly code = 'REMOTE_WORKER_OFFER_FENCED';
constructor() {
super('Remote Worker offer authority was fenced');
this.name = 'ClusterRemoteWorkerOfferFenceRejectedError';
}
}
export interface ClusterRemoteWorkerOfferClaimServiceOptions {
readonly pageSize?: number;
readonly maxPages?: number;
readonly maxClaimAttempts?: number;
readonly leaseDurationMs?: number;
readonly createEventId?: () => string;
}
function bounded(name: string, value: number, minimum: number, maximum: number): number {
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
throw new RangeError(`${name} must be between ${minimum} and ${maximum}`);
}
return value;
}
function emptyStats(): MutableClusterRemoteWorkerOfferStats {
return {
pages: 0,
candidates: 0,
plansUnavailable: 0,
placementMismatches: 0,
claimAttempts: 0,
claimRaces: 0,
};
}
function cursor(candidate: ClusterDispatchCandidate): ClusterDispatchCandidateCursor {
return Object.freeze({
priority: candidate.priority,
queuedAtMs: candidate.queuedAtMs,
attemptCreatedAtMs: candidate.attemptCreatedAtMs,
attemptId: candidate.attemptId,
});
}
export class ClusterRemoteWorkerOfferClaimService {
private readonly pageSize: number;
private readonly maxPages: number;
private readonly maxClaimAttempts: number;
private readonly leaseDurationMs: number;
private readonly createEventId: () => string;
constructor(
private readonly source: ClusterDispatchSource,
private readonly workers: Pick<WorkerSessionRepository, 'findById'>,
private readonly revisions: ClusterTaskExecutionRevisionSource,
private readonly leases: Pick<RunDispatchLeaseRepository, 'claim'>,
options: ClusterRemoteWorkerOfferClaimServiceOptions = {},
) {
if (
!source || typeof source.listClusterDispatchCandidates !== 'function' ||
typeof source.findClusterDispatchRecovery !== 'function' ||
!workers || typeof workers.findById !== 'function' ||
!revisions || typeof revisions.resolveClusterTaskExecutionRevision !== 'function' ||
!leases || typeof leases.claim !== 'function'
) throw new TypeError('Remote Worker offer service dependencies are invalid');
const allowed = new Set([
'createEventId', 'leaseDurationMs', 'maxClaimAttempts', 'maxPages', 'pageSize',
]);
if (!options || typeof options !== 'object' || Array.isArray(options) || Object.keys(options).some((key) => !allowed.has(key))) {
throw new TypeError('Remote Worker offer service options are invalid');
}
this.pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
assertRemoteDispatchPageSize(this.pageSize);
this.maxPages = bounded('Remote Worker offer maxPages', options.maxPages ?? DEFAULT_MAX_PAGES, 1, MAX_PAGES);
this.maxClaimAttempts = bounded('Remote Worker offer maxClaimAttempts', options.maxClaimAttempts ?? DEFAULT_MAX_CLAIMS, 1, MAX_CLAIMS);
this.leaseDurationMs = options.leaseDurationMs ?? DEFAULT_LEASE_MS;
assertRunDispatchLeaseDuration(this.leaseDurationMs);
this.createEventId = options.createEventId ?? randomUUID;
if (typeof this.createEventId !== 'function') {
throw new TypeError('Remote Worker offer event ID factory is invalid');
}
}
async claimNext(
principal: ClusterRemoteWorkerOfferPrincipal,
command: ClaimClusterRemoteWorkerOfferCommand,
): Promise<ClaimClusterRemoteWorkerOfferResult> {
this.assertCommand(principal, command);
const stats = emptyStats();
const recovered = await this.source.findClusterDispatchRecovery(command.offerId);
if (recovered) {
if (
recovered.lease.status !== 'leased' ||
recovered.lease.expiresAtMs <= recovered.observedAtMs ||
!recovered.workerCurrent ||
recovered.lease.workerId !== principal.workerId ||
recovered.lease.workerSessionId !== command.workerSessionId ||
recovered.lease.workerGeneration !== command.workerGeneration ||
!leaseTokenMatchesDigest(command.leaseToken, recovered.lease.leaseTokenDigest)
) throw new ClusterRemoteWorkerOfferFenceRejectedError();
const revision = await this.resolveRevision(recovered.candidate);
if (!revision) throw new ClusterRemoteWorkerOfferFenceRejectedError();
return Object.freeze({
status: 'offered' as const,
offer: this.offer(
'lease_recovery', command, recovered.candidate,
recovered.lease, revision, 0,
),
stats: Object.freeze(stats),
truncated: false,
});
}
let after: ClusterDispatchCandidateCursor | undefined;
let worker: WorkerSessionRecord | null | undefined;
let sawCandidate = false;
let sawMatch = false;
let sawRace = false;
let lastTruncated = false;
for (let pageIndex = 0; pageIndex < this.maxPages; pageIndex += 1) {
const page = await this.source.listClusterDispatchCandidates({
limit: this.pageSize,
...(after === undefined ? {} : { after }),
});
stats.pages += 1;
lastTruncated = page.truncated;
if (page.candidates.length > this.pageSize) {
throw new RangeError('Remote Worker candidate source exceeded page size');
}
worker ??= await this.workers.findById(principal.workerId);
if (
!worker || worker.sessionId !== command.workerSessionId ||
worker.generation !== command.workerGeneration ||
worker.status !== 'online' || worker.availableSlots < 1 ||
worker.leaseExpiresAtMs <= page.observedAtMs
) return this.idle('worker_unavailable', stats, false);
for (const rawCandidate of page.candidates) {
const candidate = normalizeClusterDispatchCandidate(rawCandidate);
sawCandidate = true;
stats.candidates += 1;
const revision = await this.resolveRevision(candidate);
if (!revision) {
stats.plansUnavailable += 1;
continue;
}
const placement = evaluateRemoteWorkerPlacement(
worker,
revision.placement ?? {},
page.observedAtMs,
);
if (!placement.matches) {
stats.placementMismatches += 1;
continue;
}
sawMatch = true;
if (stats.claimAttempts >= this.maxClaimAttempts) {
return this.idle('claim_budget_exhausted', stats, true);
}
const eventId = this.createEventId();
assertRunDispatchId('eventId', eventId);
stats.claimAttempts += 1;
const claim = await this.leases.claim({
runId: candidate.runId,
attemptId: candidate.attemptId,
workerId: principal.workerId,
workerSessionId: command.workerSessionId,
workerGeneration: command.workerGeneration,
leaseToken: command.leaseToken,
leaseDurationMs: this.leaseDurationMs,
eventId,
offerId: command.offerId,
});
if (claim.status === 'claimed' || claim.status === 'idempotent') {
return Object.freeze({
status: 'offered' as const,
offer: this.offer(
'new_claim', command, candidate, claim.lease, revision,
placement.score,
),
stats: Object.freeze(stats),
truncated: page.truncated,
});
}
if (claim.status === 'worker_unavailable' || claim.status === 'capacity_exhausted') {
return this.idle('worker_unavailable', stats, false);
}
stats.claimRaces += 1;
sawRace = true;
}
if (!page.truncated || page.candidates.length === 0) break;
const last = page.candidates.at(-1);
if (!last) break;
const next = page.next ?? cursor(last);
if (after && next.attemptId === after.attemptId) {
throw new Error('Remote Worker candidate cursor did not advance');
}
after = next;
}
if (lastTruncated) return this.idle('scan_budget_exhausted', stats, true);
if (!sawCandidate) return this.idle('no_candidates', stats, false);
if (stats.plansUnavailable === stats.candidates) return this.idle('plans_unavailable', stats, false);
return this.idle(sawRace ? 'claim_raced' : sawMatch ? 'claim_raced' : 'no_match', stats, false);
}
private async resolveRevision(
candidate: ClusterDispatchCandidate,
): Promise<ClusterTaskExecutionRevision | null> {
let sourceRevision: number;
try {
sourceRevision = parseTaskDefinitionRevisionRef(candidate.taskRevision).revision;
} catch {
return null;
}
const revision = await this.revisions.resolveClusterTaskExecutionRevision({
projectId: candidate.projectId,
taskId: candidate.taskId,
sourceRevision,
});
if (
!revision || revision.projectId !== candidate.projectId ||
revision.taskId !== candidate.taskId ||
revision.taskRevision !== candidate.taskRevision
) return null;
return revision;
}
private offer(
deliveryKind: ClusterRemoteExecutionOffer['deliveryKind'],
command: ClaimClusterRemoteWorkerOfferCommand,
candidate: ClusterDispatchCandidate,
lease: Extract<ClaimRunDispatchLeaseResult, { lease: unknown }>['lease'],
revision: ClusterTaskExecutionRevision,
placementScore: number,
): ClusterRemoteExecutionOffer {
return createClusterRemoteExecutionOffer({
offerId: command.offerId,
deliveryKind,
executionDigest: revision.contentDigest,
candidate,
worker: {
workerId: lease.workerId,
sessionId: lease.workerSessionId,
generation: lease.workerGeneration,
},
lease,
leaseToken: command.leaseToken,
executionRevision: revision,
placementScore,
});
}
private idle(
reason: Extract<ClaimClusterRemoteWorkerOfferResult, { status: 'idle' }>['reason'],
stats: ClusterRemoteWorkerOfferStats,
truncated: boolean,
): ClaimClusterRemoteWorkerOfferResult {
return Object.freeze({
status: 'idle' as const,
reason,
stats: Object.freeze({ ...stats }),
truncated,
});
}
private assertCommand(
principal: ClusterRemoteWorkerOfferPrincipal,
command: ClaimClusterRemoteWorkerOfferCommand,
): void {
if (!principal || typeof principal !== 'object' || Array.isArray(principal)) {
throw new TypeError('Remote Worker offer principal is invalid');
}
assertWorkerId(principal.workerId);
if (!command || typeof command !== 'object' || Array.isArray(command)) {
throw new TypeError('Remote Worker offer command is invalid');
}
const keys = Object.keys(command).sort().join(',');
if (keys !== 'leaseToken,offerId,workerGeneration,workerSessionId') {
throw new TypeError('Remote Worker offer command shape is invalid');
}
assertWorkerSessionId(command.workerSessionId);
if (!Number.isSafeInteger(command.workerGeneration) || command.workerGeneration < 1) {
throw new RangeError('Remote Worker offer generation is invalid');
}
assertRunDispatchId('offerId', command.offerId);
assertRunDispatchLeaseToken(command.leaseToken);
}
}
export function createRemoteWorkerLeaseToken(): string {
return randomBytes(32).toString('base64url');
}
@@ -0,0 +1,58 @@
// Remote execution owns fenced Worker lease renewal, release, and timeout authority.
import { randomUUID } from 'node:crypto';
import {
RemoteWorkerLeaseControlUnavailableError,
assertRemoteWorkerLeaseControlDuration,
normalizeRemoteWorkerLeaseControlCommand,
normalizeRemoteWorkerLeaseControlResult,
type RemoteWorkerLeaseControlCommand,
type RemoteWorkerLeaseControlRepository,
type RemoteWorkerLeaseControlResult,
} from '@qinglong/runtime-core/remote-worker-lease-control';
export interface ClusterRemoteWorkerLeaseControlServiceOptions {
readonly leaseDurationMs?: number;
readonly createEventId?: () => string;
}
function eventId(factory: () => string): string {
const value = factory();
if (
typeof value !== 'string' || value.length < 1 || value.length > 36 ||
/[\u0000-\u001f\u007f]/.test(value)
) throw new RemoteWorkerLeaseControlUnavailableError();
return value;
}
export class ClusterRemoteWorkerLeaseControlService {
private readonly leaseDurationMs: number;
private readonly createEventId: () => string;
constructor(
private readonly repository: RemoteWorkerLeaseControlRepository,
options: ClusterRemoteWorkerLeaseControlServiceOptions = {},
) {
if (
typeof repository?.control !== 'function' ||
(options.createEventId !== undefined &&
typeof options.createEventId !== 'function')
) throw new TypeError('Remote Worker lease control service is invalid');
const leaseDurationMs = options.leaseDurationMs ?? 30_000;
assertRemoteWorkerLeaseControlDuration(leaseDurationMs);
this.leaseDurationMs = leaseDurationMs;
this.createEventId = options.createEventId ?? randomUUID;
}
async control(
value: RemoteWorkerLeaseControlCommand,
): Promise<Readonly<RemoteWorkerLeaseControlResult>> {
const command = normalizeRemoteWorkerLeaseControlCommand(value);
return normalizeRemoteWorkerLeaseControlResult(
await this.repository.control(Object.freeze({
...command,
leaseDurationMs: this.leaseDurationMs,
timeoutEventId: eventId(this.createEventId),
})),
);
}
}
@@ -0,0 +1,122 @@
// Remote execution owns offer-bound Secret delivery without retaining plaintext authority.
import {
InvalidRemoteWorkerSecretDeliveryError,
RemoteWorkerSecretDeliveryFenceRejectedError,
RemoteWorkerSecretDeliveryUnavailableError,
createRemoteWorkerSecretDeliveryResponseBody,
normalizeRemoteWorkerSecretDeliveryAuthority,
normalizeRemoteWorkerSecretDeliveryCommand,
type RemoteWorkerSecretDeliveryAuthorityRepository,
type RemoteWorkerSecretDeliveryCommand,
type RemoteWorkerSecretDeliveryResult,
type RemoteWorkerSecretValueProvider,
} from '@qinglong/runtime-core/remote-secret-delivery';
export interface ClusterRemoteWorkerSecretDeliveryPrincipal {
readonly workerId: string;
}
export type ClusterRemoteWorkerSecretDeliveryCommand = Omit<
RemoteWorkerSecretDeliveryCommand,
'workerId'
>;
export class ClusterRemoteWorkerSecretDeliveryService {
constructor(
private readonly authority: RemoteWorkerSecretDeliveryAuthorityRepository,
private readonly secrets: RemoteWorkerSecretValueProvider,
) {
if (
!authority ||
typeof authority.authorize !== 'function' ||
!secrets ||
typeof secrets.resolve !== 'function'
) throw new TypeError('Remote Worker Secret delivery service is invalid');
}
async deliver(
principal: ClusterRemoteWorkerSecretDeliveryPrincipal,
input: ClusterRemoteWorkerSecretDeliveryCommand,
): Promise<Readonly<RemoteWorkerSecretDeliveryResult>> {
if (
!principal ||
typeof principal !== 'object' ||
Array.isArray(principal) ||
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(principal.workerId)
) throw new TypeError('Remote Worker Secret delivery principal is invalid');
const command = normalizeRemoteWorkerSecretDeliveryCommand({
...input,
workerId: principal.workerId,
});
let authorized;
try {
authorized = normalizeRemoteWorkerSecretDeliveryAuthority(
await this.authority.authorize(command),
);
if (
authorized.workerId !== command.workerId ||
authorized.workerSessionId !== command.workerSessionId ||
authorized.workerGeneration !== command.workerGeneration ||
authorized.runId !== command.runId ||
authorized.attemptId !== command.attemptId ||
authorized.projectId !== command.projectId ||
authorized.taskId !== command.taskId ||
authorized.taskRevision !== command.taskRevision ||
authorized.executionDigest !== command.executionDigest ||
authorized.offerId !== command.offerId ||
authorized.leaseGeneration !== command.leaseGeneration ||
authorized.leaseVersion !== command.expectedLeaseVersion ||
JSON.stringify(authorized.secretRefs) !== JSON.stringify(command.secretRefs)
) throw new InvalidRemoteWorkerSecretDeliveryError(
'repository authority does not match command',
);
} catch (error) {
if (
error instanceof RemoteWorkerSecretDeliveryFenceRejectedError ||
error instanceof RemoteWorkerSecretDeliveryUnavailableError
) throw error;
throw new RemoteWorkerSecretDeliveryUnavailableError();
}
let resolution;
try {
resolution = await this.secrets.resolve(authorized);
} catch {
throw new RemoteWorkerSecretDeliveryUnavailableError();
}
if (!resolution) throw new RemoteWorkerSecretDeliveryUnavailableError();
try {
if (
typeof resolution !== 'object' ||
Array.isArray(resolution) ||
Object.keys(resolution).some((key) => key !== 'values' && key !== 'dispose') ||
(resolution.dispose !== undefined &&
typeof resolution.dispose !== 'function')
) throw new InvalidRemoteWorkerSecretDeliveryError(
'provider response shape is invalid',
);
const body = createRemoteWorkerSecretDeliveryResponseBody({
runId: authorized.runId,
attemptId: authorized.attemptId,
offerId: authorized.offerId,
executionDigest: authorized.executionDigest,
values: resolution.values,
}, authorized.secretRefs);
return Object.freeze({
runId: body.runId,
attemptId: body.attemptId,
offerId: body.offerId,
executionDigest: body.executionDigest,
values: body.values,
...(resolution.dispose === undefined
? {}
: { dispose: resolution.dispose }),
});
} catch (error) {
try { await resolution.dispose?.(); } catch { /* preserve root */ }
if (error instanceof InvalidRemoteWorkerSecretDeliveryError) {
throw new RemoteWorkerSecretDeliveryUnavailableError();
}
throw error;
}
}
}
@@ -0,0 +1,103 @@
// Remote execution owns the least-privilege assembly of Worker-facing runtime capabilities.
import type { PostgresPool } from '@qinglong/runtime-core';
import type { RemoteWorkerSecretValueProvider } from '@qinglong/runtime-core/remote-secret-delivery';
import {
PostgresClusterDispatchSource,
PostgresRemoteRunActivationRepository,
PostgresRemoteWorkerCompletionRepository,
PostgresRemoteWorkerLeaseControlRepository,
PostgresRemoteWorkerSecretDeliveryAuthorityRepository,
PostgresRunDispatchLeaseRepository,
PostgresTaskExecutionRevisionSource,
PostgresWorkerSessionRepository,
} from '@qinglong/cluster-postgres/runtime';
import {
ClusterRemoteWorkerOfferClaimService,
} from './remoteWorkerDispatcher';
import {
ClusterRemoteRunActivationService,
} from './remoteRunActivationService';
import {
ClusterRemoteWorkerSecretDeliveryService,
} from './remoteWorkerSecretDeliveryService';
import {
ClusterRemoteWorkerArtifactService,
ClusterRemoteWorkerCompletionService,
type ClusterRemoteWorkerArtifactStore,
} from './remoteWorkerCompletionService';
import {
ClusterRemoteWorkerLeaseControlService,
} from './remoteWorkerLeaseControlService';
import type { WorkerIngressPipelineOptions } from '../worker-ingress/workerIngressPipeline';
export interface ClusterWorkerRuntimeDependencies {
readonly artifactStore: ClusterRemoteWorkerArtifactStore;
readonly secretProvider?: RemoteWorkerSecretValueProvider;
}
/**
* The in-process capability boundary from the runtime authority to the
* Worker-facing transport. It exposes reviewed operations, never the runtime
* Pool or mutation repositories.
*/
export interface ClusterWorkerRuntimePort {
readonly offers: NonNullable<WorkerIngressPipelineOptions['offers']>;
readonly activation: NonNullable<WorkerIngressPipelineOptions['activation']>;
readonly secrets?: NonNullable<WorkerIngressPipelineOptions['secrets']>;
readonly artifacts: NonNullable<WorkerIngressPipelineOptions['artifacts']>;
readonly completion: NonNullable<WorkerIngressPipelineOptions['completion']>;
readonly leaseControl: NonNullable<
WorkerIngressPipelineOptions['leaseControl']
>;
}
export function createClusterWorkerRuntimePort(
pool: PostgresPool,
dependencies: ClusterWorkerRuntimeDependencies,
): Readonly<ClusterWorkerRuntimePort> {
if (!pool || typeof pool.query !== 'function') {
throw new TypeError('Cluster Worker runtime Pool is invalid');
}
if (
!dependencies ||
typeof dependencies !== 'object' ||
Array.isArray(dependencies)
) {
throw new TypeError('Cluster Worker runtime dependencies are invalid');
}
const workerSessions = new PostgresWorkerSessionRepository(pool);
const completionRepository =
new PostgresRemoteWorkerCompletionRepository(pool);
const secretProvider = dependencies.secretProvider;
return Object.freeze({
offers: new ClusterRemoteWorkerOfferClaimService(
new PostgresClusterDispatchSource(pool),
workerSessions,
new PostgresTaskExecutionRevisionSource(pool),
new PostgresRunDispatchLeaseRepository(pool),
),
activation: new ClusterRemoteRunActivationService(
new PostgresRemoteRunActivationRepository(pool),
),
...(secretProvider === undefined
? {}
: {
secrets: new ClusterRemoteWorkerSecretDeliveryService(
new PostgresRemoteWorkerSecretDeliveryAuthorityRepository(pool),
secretProvider,
),
}),
artifacts: new ClusterRemoteWorkerArtifactService(
completionRepository,
dependencies.artifactStore,
),
completion: new ClusterRemoteWorkerCompletionService(
completionRepository,
dependencies.artifactStore,
),
leaseControl: new ClusterRemoteWorkerLeaseControlService(
new PostgresRemoteWorkerLeaseControlRepository(pool),
),
});
}
@@ -0,0 +1,136 @@
// Run owns bounded convergence of durable cancellation intent to terminal state.
import type {
ClusterRunCancellationConvergenceCoordinator,
ClusterRunCancellationConvergenceCycleResult,
} from '@qinglong/runtime-core/cluster-run-cancellation-convergence';
export interface ClusterRunCancellationConvergenceLifecycleOptions {
readonly intervalMs: number;
readonly stopTimeoutMs: number;
readonly onDiagnostic?: (
error: unknown,
summary?: Readonly<ClusterRunCancellationConvergenceCycleResult>,
) => void | Promise<void>;
}
export interface ClusterRunCancellationConvergenceLifecycleStopSummary {
readonly status: 'stopped' | 'timed_out';
}
/** One constant-cost cadence for all pending non-executing Run cancellations. */
export class ClusterRunCancellationConvergenceLifecycle {
private timer: NodeJS.Timeout | undefined;
private inFlight:
| Promise<Readonly<ClusterRunCancellationConvergenceCycleResult>>
| undefined;
private stopPromise:
| Promise<ClusterRunCancellationConvergenceLifecycleStopSummary>
| undefined;
private running = false;
private stopping = false;
constructor(
private readonly coordinator: Pick<
ClusterRunCancellationConvergenceCoordinator,
'reconcile'
>,
private readonly options: ClusterRunCancellationConvergenceLifecycleOptions,
) {
if (
typeof coordinator?.reconcile !== 'function' ||
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
!Number.isSafeInteger(options.intervalMs) ||
options.intervalMs < 250 ||
options.intervalMs > 60 * 60_000 ||
!Number.isSafeInteger(options.stopTimeoutMs) ||
options.stopTimeoutMs < 100 ||
options.stopTimeoutMs > 30_000 ||
(options.onDiagnostic !== undefined &&
typeof options.onDiagnostic !== 'function')
) {
throw new TypeError('Cluster Run cancellation lifecycle options are invalid');
}
}
start(): 'started' {
if (!this.running && !this.stopping) {
this.running = true;
this.schedule();
}
return 'started';
}
runOnce(): Promise<Readonly<ClusterRunCancellationConvergenceCycleResult>> {
if (this.stopping) {
return Promise.reject(
new Error('Cluster Run cancellation lifecycle is stopping'),
);
}
if (this.inFlight) return this.inFlight;
const work = this.coordinator.reconcile().finally(() => {
if (this.inFlight === work) this.inFlight = undefined;
});
this.inFlight = work;
return work;
}
stopAndDrain(): Promise<ClusterRunCancellationConvergenceLifecycleStopSummary> {
if (this.stopPromise) return this.stopPromise;
this.stopping = true;
this.running = false;
if (this.timer) clearTimeout(this.timer);
this.timer = undefined;
this.stopPromise = (async () => {
const work = this.inFlight;
if (!work) return Object.freeze({ status: 'stopped' as const });
let timeout: NodeJS.Timeout | undefined;
try {
return await Promise.race([
work.then(
() => Object.freeze({ status: 'stopped' as const }),
() => Object.freeze({ status: 'stopped' as const }),
),
new Promise<ClusterRunCancellationConvergenceLifecycleStopSummary>(
(resolve) => {
timeout = setTimeout(
() => resolve(Object.freeze({ status: 'timed_out' as const })),
this.options.stopTimeoutMs,
);
timeout.unref?.();
},
),
]);
} finally {
if (timeout) clearTimeout(timeout);
}
})();
return this.stopPromise;
}
private schedule(): void {
if (!this.running || this.timer) return;
this.timer = setTimeout(() => {
this.timer = undefined;
if (!this.running) return;
void this.runOnce()
.then((summary) => this.diagnostic(undefined, summary))
.catch((error) => this.diagnostic(error))
.finally(() => this.schedule());
}, this.options.intervalMs);
this.timer.unref?.();
}
private async diagnostic(
error: unknown,
summary?: Readonly<ClusterRunCancellationConvergenceCycleResult>,
): Promise<void> {
if (this.stopping) return;
try {
await this.options.onDiagnostic?.(error, summary);
} catch {
// Diagnostics cannot own or stop convergence.
}
}
}
@@ -0,0 +1,116 @@
// Run owns its Policy-fenced durable cancellation mutation route.
import {
CLUSTER_RUN_CANCELLATION_SCHEMA,
ClusterRunCancellationFenceRejectedError,
ClusterRunCancellationNotFoundError,
ClusterRunCancellationUnavailableError,
InvalidClusterRunCancellationError,
createClusterRunCancellationResponseBody,
parseClusterRunCancellationRequestBody,
type ClusterRunCancellationRepository,
} from '@qinglong/runtime-core/cluster-run-cancellation';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../transport/routeRegistry';
export const CLUSTER_CONTROL_RUN_CANCELLATION_ROUTE = Object.freeze({
method: 'POST' as const,
path: '/api/v3/projects/{projectId}/runs/{runId}/cancellation',
operationId: 'run.cancel',
permission: 'run.stop',
projectParameter: 'projectId',
});
export type ClusterRunCancellationEventIdFactory = () => string;
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
/**
* Publishes a durable cancellation command. Authentication, authorization and
* the first audit complete in admission; the repository revalidates the exact
* policy fence in the same transaction that writes the Run intent and Event.
*/
export function createClusterControlRunCancellationRoute(
repository: ClusterRunCancellationRepository,
createEventId: ClusterRunCancellationEventIdFactory,
): Readonly<ClusterControlRouteDefinition> {
if (
!repository ||
typeof repository.requestUserCancellation !== 'function' ||
typeof createEventId !== 'function'
) {
throw new TypeError('Cluster-control Run cancellation route is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_RUN_CANCELLATION_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
let body;
try {
body = parseClusterRunCancellationRequestBody(
authorized.request.body,
);
} catch (error) {
if (error instanceof InvalidClusterRunCancellationError) {
return response(400, {
code: 'invalid_run_cancellation_request',
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
});
}
return response(503, { code: 'run_cancellation_unavailable' });
}
const projectId = authorized.projectId;
const runId = parameters.runId;
if (
projectId === null ||
typeof runId !== 'string' ||
runId.length < 1 ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null
) {
return response(503, { code: 'run_cancellation_unavailable' });
}
try {
const result = await repository.requestUserCancellation({
projectId,
runId,
mutationId: body.mutationId,
eventId: createEventId(),
subject: authorized.principal.subject,
policyFence: authorized.policyFence,
});
return response(
result.status === 'accepted' ? 202 : 200,
createClusterRunCancellationResponseBody(result),
);
} catch (error) {
if (error instanceof ClusterRunCancellationNotFoundError) {
return response(404, { code: 'run_not_found' });
}
if (error instanceof ClusterRunCancellationFenceRejectedError) {
return response(409, {
code: 'run_cancellation_fence_rejected',
reason: error.reason,
});
}
if (
error instanceof InvalidClusterRunCancellationError ||
error instanceof ClusterRunCancellationUnavailableError
) {
return response(503, { code: 'run_cancellation_unavailable' });
}
return response(503, { code: 'run_cancellation_unavailable' });
}
},
});
}
@@ -0,0 +1,124 @@
import {
BoundedRunEventListProjectionUnavailableError,
InvalidBoundedRunEventListProjectionError,
executeBoundedRunEventListProjection,
} from '@qinglong/runtime-core/bounded-run-event-list-projection';
import type { RunRepositoryReader } from '@qinglong/runtime-core/run-repository';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../transport/routeRegistry';
export const CLUSTER_CONTROL_RUN_EVENT_LIST_ROUTE = Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/runs/{runId}/events',
operationId: 'run.events.list',
permission: 'run.read',
projectParameter: 'projectId',
allowedQuery: Object.freeze(['after_sequence', 'limit']),
});
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function parseQuery(
query: Readonly<Record<string, readonly string[]>>,
): Readonly<{ afterSequence?: number; limit?: number }> {
const afterValues = query.after_sequence;
const limitValues = query.limit;
if (
(afterValues !== undefined && afterValues.length !== 1) ||
(limitValues !== undefined && limitValues.length !== 1)
) {
throw new TypeError();
}
const rawAfter = afterValues?.[0];
const afterSequence = rawAfter === undefined ? undefined : Number(rawAfter);
const rawLimit = limitValues?.[0];
const limit = rawLimit === undefined ? undefined : Number(rawLimit);
if (
(rawAfter !== undefined &&
(!Number.isSafeInteger(afterSequence) ||
Number(afterSequence) < 0 ||
Number(afterSequence) > 2_147_483_647 ||
String(afterSequence) !== rawAfter)) ||
(rawLimit !== undefined &&
(!Number.isSafeInteger(limit) ||
Number(limit) < 1 ||
Number(limit) > 64 ||
String(limit) !== rawLimit))
) {
throw new TypeError();
}
return Object.freeze({
...(afterSequence === undefined ? {} : { afterSequence }),
...(limit === undefined ? {} : { limit }),
});
}
function validateRunEventListQuery(
query: Readonly<Record<string, readonly string[]>>,
): void {
parseQuery(query);
}
export function createClusterControlRunEventListRoute(
runs: Pick<RunRepositoryReader, 'findRunById' | 'listEvents'>,
): Readonly<ClusterControlRouteDefinition> {
if (
!runs ||
typeof runs.findRunById !== 'function' ||
typeof runs.listEvents !== 'function'
) {
throw new TypeError('Cluster-control Run event list repository is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_RUN_EVENT_LIST_ROUTE,
validateQuery: validateRunEventListQuery,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
if (authorized.projectId === null) {
return response(503, { code: 'run_event_list_unavailable' });
}
let input;
try {
input = parseQuery(authorized.request.query);
} catch {
return response(400, { code: 'invalid_run_event_list_query' });
}
try {
const result = await executeBoundedRunEventListProjection(
runs,
authorized.projectId,
parameters.runId!,
input,
);
if (!result.found) {
return response(404, { code: 'run_not_found' });
}
const { found: _found, ...timeline } = result;
return response(200, { ...timeline });
} catch (error) {
if (
error instanceof InvalidBoundedRunEventListProjectionError ||
error instanceof BoundedRunEventListProjectionUnavailableError
) {
return response(503, { code: 'run_event_list_unavailable' });
}
throw error;
}
},
});
}
@@ -0,0 +1,130 @@
import {
BoundedRunListProjectionUnavailableError,
InvalidBoundedRunListProjectionError,
executeBoundedRunListProjection,
} from '@qinglong/runtime-core/bounded-run-list-projection';
import type { ProjectRunListReader } from '@qinglong/runtime-core/project-run-list';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
} from '../transport/routeRegistry';
export const CLUSTER_CONTROL_RUN_LIST_ROUTE = Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/runs',
operationId: 'run.list',
permission: 'run.read',
projectParameter: 'projectId',
allowedQuery: Object.freeze([
'after_created_at_ms',
'after_run_id',
'limit',
]),
});
const RUN_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function parseQuery(
query: Readonly<Record<string, readonly string[]>>,
): Readonly<{
limit?: number;
after?: Readonly<{ createdAtMs: number; runId: string }>;
}> {
const limitValues = query.limit;
const createdAtValues = query.after_created_at_ms;
const runIdValues = query.after_run_id;
if (
(limitValues !== undefined && limitValues.length !== 1) ||
(createdAtValues !== undefined && createdAtValues.length !== 1) ||
(runIdValues !== undefined && runIdValues.length !== 1) ||
(createdAtValues === undefined) !== (runIdValues === undefined)
) {
throw new TypeError();
}
const rawLimit = limitValues?.[0];
const limit = rawLimit === undefined ? undefined : Number(rawLimit);
if (
rawLimit !== undefined &&
(!Number.isSafeInteger(limit) ||
Number(limit) < 1 ||
Number(limit) > 64 ||
String(limit) !== rawLimit)
) {
throw new TypeError();
}
const rawCreatedAtMs = createdAtValues?.[0];
const runId = runIdValues?.[0];
if (rawCreatedAtMs === undefined || runId === undefined) {
return Object.freeze({ ...(limit === undefined ? {} : { limit }) });
}
const createdAtMs = Number(rawCreatedAtMs);
if (
!Number.isSafeInteger(createdAtMs) ||
createdAtMs < 0 ||
String(createdAtMs) !== rawCreatedAtMs ||
!RUN_ID_PATTERN.test(runId)
) {
throw new TypeError();
}
return Object.freeze({
...(limit === undefined ? {} : { limit }),
after: Object.freeze({ createdAtMs, runId }),
});
}
function validateRunListQuery(
query: Readonly<Record<string, readonly string[]>>,
): void {
parseQuery(query);
}
export function createClusterControlRunListRoute(
runs: ProjectRunListReader,
): Readonly<ClusterControlRouteDefinition> {
if (!runs || typeof runs.listRunsByProject !== 'function') {
throw new TypeError('Cluster-control Run list repository is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_RUN_LIST_ROUTE,
validateQuery: validateRunListQuery,
async handle(authorized: ClusterControlAuthorizedOperationRequest) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
if (authorized.projectId === null) {
return response(503, { code: 'run_list_unavailable' });
}
let input;
try {
input = parseQuery(authorized.request.query);
} catch {
return response(400, { code: 'invalid_run_list_query' });
}
try {
const result = await executeBoundedRunListProjection(
runs,
authorized.projectId,
input,
);
return response(200, { ...result });
} catch (error) {
if (
error instanceof InvalidBoundedRunListProjectionError ||
error instanceof BoundedRunListProjectionUnavailableError
) {
return response(503, { code: 'run_list_unavailable' });
}
throw error;
}
},
});
}
@@ -0,0 +1,165 @@
// Run owns its bounded read projection and masks cross-Project storage facts.
import {
EXECUTION_ORIGINS,
RUN_STATUSES,
type ExecutionOrigin,
type ExecutionOwner,
type RunRecord,
type RunRepositoryReader,
type RunStatus,
} from '@qinglong/runtime-core';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../transport/routeRegistry';
export interface ClusterControlRunReadRepository
extends Pick<RunRepositoryReader, 'findRunById'> {}
export interface ClusterControlRunView {
readonly id: string;
readonly projectId: string;
readonly taskId: string;
readonly taskRevision: string;
readonly status: RunStatus;
readonly version: number;
readonly eventSequence: number;
readonly priority: number;
readonly executionOrigin: ExecutionOrigin;
readonly executionOwner: ExecutionOwner;
readonly createdAtMs: number;
readonly queuedAtMs: number | null;
readonly startedAtMs: number | null;
readonly finishedAtMs: number | null;
}
export const CLUSTER_CONTROL_RUN_READ_ROUTE = Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/runs/{runId}',
operationId: 'run.get',
permission: 'run.read',
projectParameter: 'projectId',
});
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/;
function boundedText(value: unknown, maximum: number): value is string {
return (
typeof value === 'string' &&
value.length > 0 &&
value.length <= maximum &&
!CONTROL_CHARACTER_PATTERN.test(value)
);
}
function nonNegativeInteger(value: unknown): value is number {
return Number.isSafeInteger(value) && Number(value) >= 0;
}
function optionalTimestamp(value: unknown): value is number | undefined {
return value === undefined || nonNegativeInteger(value);
}
function projectRunView(
run: RunRecord,
runId: string,
): Readonly<ClusterControlRunView> | null {
if (
!run ||
typeof run !== 'object' ||
Array.isArray(run) ||
run.id !== runId ||
!boundedText(run.id, 128) ||
!boundedText(run.projectId, 128) ||
!boundedText(run.taskId, 255) ||
!boundedText(run.taskRevision, 255) ||
!RUN_STATUSES.includes(run.status) ||
!EXECUTION_ORIGINS.includes(run.executionOrigin) ||
(run.executionOwner !== 'legacy' && run.executionOwner !== 'runtime') ||
!Number.isSafeInteger(run.version) ||
run.version < 0 ||
!nonNegativeInteger(run.eventSequence) ||
!Number.isSafeInteger(run.priority) ||
!nonNegativeInteger(run.createdAtMs) ||
!optionalTimestamp(run.queuedAtMs) ||
!optionalTimestamp(run.startedAtMs) ||
!optionalTimestamp(run.finishedAtMs)
) {
return null;
}
return Object.freeze({
id: run.id,
projectId: run.projectId,
taskId: run.taskId,
taskRevision: run.taskRevision,
status: run.status,
version: run.version,
eventSequence: run.eventSequence,
priority: run.priority,
executionOrigin: run.executionOrigin,
executionOwner: run.executionOwner,
createdAtMs: run.createdAtMs,
queuedAtMs: run.queuedAtMs ?? null,
startedAtMs: run.startedAtMs ?? null,
finishedAtMs: run.finishedAtMs ?? null,
});
}
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
/**
* Defines the first reviewed cluster-control business route. The response is a
* deliberately low-sensitive projection: refs, trigger identity, request IDs,
* executor handles, error summaries and output locations never cross the wire.
*/
export function createClusterControlRunReadRoute(
repository: ClusterControlRunReadRepository,
): Readonly<ClusterControlRouteDefinition> {
if (!repository || typeof repository.findRunById !== 'function') {
throw new TypeError('Cluster-control Run read repository is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_RUN_READ_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
const runId = parameters.runId;
if (!boundedText(runId, 128)) {
return response(503, { code: 'run_query_unavailable' });
}
let run: RunRecord | null;
try {
run = await repository.findRunById(runId);
} catch {
return response(503, { code: 'run_query_unavailable' });
}
if (!run) {
return response(404, { code: 'run_not_found' });
}
const view = projectRunView(run, runId);
if (!view) {
return response(503, { code: 'run_query_unavailable' });
}
if (view.projectId !== authorized.projectId) {
return response(404, { code: 'run_not_found' });
}
return response(200, { run: view });
},
});
}
export * from './runCancellationRoute';
export * from './runListRoute';
export * from './runEventListRoute';
export * from './runStepListRoute';
@@ -0,0 +1,130 @@
import {
BoundedRunStepListProjectionUnavailableError,
InvalidBoundedRunStepListProjectionError,
executeBoundedRunStepListProjection,
} from '@qinglong/runtime-core/bounded-run-step-list-projection';
import type { RunRepositoryReader } from '@qinglong/runtime-core/run-repository';
import type { StepRunRepository } from '@qinglong/runtime-core/step-run';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../transport/routeRegistry';
export const CLUSTER_CONTROL_RUN_STEP_LIST_ROUTE = Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/runs/{runId}/steps',
operationId: 'run.steps.list',
permission: 'run.read',
projectParameter: 'projectId',
allowedQuery: Object.freeze(['after_step_key', 'after_step_run_id', 'limit']),
});
const IDENTITY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function parseQuery(query: Readonly<Record<string, readonly string[]>>) {
const stepKeyValues = query.after_step_key;
const stepRunIdValues = query.after_step_run_id;
const limitValues = query.limit;
if (
(stepKeyValues !== undefined && stepKeyValues.length !== 1) ||
(stepRunIdValues !== undefined && stepRunIdValues.length !== 1) ||
(limitValues !== undefined && limitValues.length !== 1) ||
(stepKeyValues === undefined) !== (stepRunIdValues === undefined)
) {
throw new TypeError();
}
const stepKey = stepKeyValues?.[0];
const stepRunId = stepRunIdValues?.[0];
const rawLimit = limitValues?.[0];
const limit = rawLimit === undefined ? undefined : Number(rawLimit);
if (
(stepKey !== undefined && !IDENTITY_PATTERN.test(stepKey)) ||
(stepRunId !== undefined && !IDENTITY_PATTERN.test(stepRunId)) ||
(rawLimit !== undefined &&
(!Number.isSafeInteger(limit) ||
Number(limit) < 1 ||
Number(limit) > 64 ||
String(limit) !== rawLimit))
) {
throw new TypeError();
}
return Object.freeze({
...(limit === undefined ? {} : { limit }),
...(stepKey === undefined || stepRunId === undefined
? {}
: { after: Object.freeze({ stepKey, stepRunId }) }),
});
}
function validateRunStepListQuery(
query: Readonly<Record<string, readonly string[]>>,
): void {
parseQuery(query);
}
export function createClusterControlRunStepListRoute(
runs: Pick<RunRepositoryReader, 'findRunById'>,
stepRuns: Pick<StepRunRepository, 'listByRun'>,
): Readonly<ClusterControlRouteDefinition> {
if (
!runs ||
typeof runs.findRunById !== 'function' ||
!stepRuns ||
typeof stepRuns.listByRun !== 'function'
) {
throw new TypeError('Cluster-control Run Step list repository is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_RUN_STEP_LIST_ROUTE,
validateQuery: validateRunStepListQuery,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
if (authorized.projectId === null) {
return response(503, { code: 'run_step_list_unavailable' });
}
let input;
try {
input = parseQuery(authorized.request.query);
} catch {
return response(400, { code: 'invalid_run_step_list_query' });
}
try {
const result = await executeBoundedRunStepListProjection(
runs,
stepRuns,
authorized.projectId,
parameters.runId!,
input,
);
if (!result.found) {
return response(404, { code: 'run_not_found' });
}
const { found: _found, ...page } = result;
return response(200, { ...page });
} catch (error) {
if (
error instanceof InvalidBoundedRunStepListProjectionError ||
error instanceof BoundedRunStepListProjectionUnavailableError
) {
return response(503, { code: 'run_step_list_unavailable' });
}
throw error;
}
},
});
}
@@ -0,0 +1,45 @@
// Scheduling owns the bounded Cron expression adapter used by the Cluster cadence.
import type {
LocalCronNextOccurrence,
LocalCronSchedule,
} from '@qinglong/runtime-core/local-scheduler';
interface CronerJob {
nextRun(after: Date): Date | null;
stop(): void;
}
interface CronerConstructor {
new (
expression: string,
options: Readonly<{
timezone: string;
paused: true;
unref: true;
}>,
): CronerJob;
}
export const cronerClusterNextOccurrence: LocalCronNextOccurrence = (
schedule: LocalCronSchedule,
afterMs: number,
): number => {
let job: CronerJob | undefined;
try {
const { Cron } = require('croner') as Readonly<{
Cron: CronerConstructor;
}>;
job = new Cron(schedule.expression, {
timezone: schedule.timezone,
paused: true,
unref: true,
});
const next = job.nextRun(new Date(afterMs));
if (!(next instanceof Date)) {
throw new Error('cron has no next occurrence');
}
return next.getTime();
} finally {
job?.stop();
}
};
@@ -0,0 +1,69 @@
// Scheduling owns recovery and lost-retry ordering inside the shared cadence.
import type {
ClusterControlStartupRecoverySummary,
ClusterRunLostRetryPageResult,
} from '@qinglong/runtime-core';
import type {
ClusterSchedulerCoordinator,
ClusterSchedulerCycleSummary,
} from './scheduler';
export interface ClusterRuntimeSchedulerMaintenanceSummary {
readonly recovery: Readonly<ClusterControlStartupRecoverySummary>;
readonly lostRetry: Readonly<ClusterRunLostRetryPageResult>;
}
/**
* Reuses the scheduler's single non-overlapping cadence for runtime recovery
* and lost retry. It owns no timer, connection, cursor or per-Run state.
*/
export class ClusterRuntimeSchedulerCoordinator {
private inFlight: Promise<ClusterSchedulerCycleSummary> | undefined;
private latestMaintenance:
| Readonly<ClusterRuntimeSchedulerMaintenanceSummary>
| undefined;
constructor(
private readonly recovery: Readonly<{
reconcile(): Promise<ClusterControlStartupRecoverySummary>;
}>,
private readonly lostRetry: Readonly<{
reconcile(): Promise<Readonly<ClusterRunLostRetryPageResult>>;
}>,
private readonly scheduler: Pick<
ClusterSchedulerCoordinator,
'scheduleOnce'
>,
) {
if (
typeof recovery?.reconcile !== 'function' ||
typeof lostRetry?.reconcile !== 'function' ||
typeof scheduler?.scheduleOnce !== 'function'
) {
throw new TypeError('Cluster runtime scheduler coordinator is invalid');
}
}
scheduleOnce(): Promise<ClusterSchedulerCycleSummary> {
if (this.inFlight) return this.inFlight;
const work = this.runCycle().finally(() => {
if (this.inFlight === work) this.inFlight = undefined;
});
this.inFlight = work;
return work;
}
latestMaintenanceSummary():
| Readonly<ClusterRuntimeSchedulerMaintenanceSummary>
| undefined {
return this.latestMaintenance;
}
private async runCycle(): Promise<ClusterSchedulerCycleSummary> {
const recovery = await this.recovery.reconcile();
const lostRetry = await this.lostRetry.reconcile();
this.latestMaintenance = Object.freeze({ recovery, lostRetry });
return this.scheduler.scheduleOnce();
}
}
@@ -0,0 +1,294 @@
// Scheduling owns bounded trigger claiming and the single non-overlapping lifecycle timer.
import { randomUUID } from 'node:crypto';
import {
MAX_CLUSTER_SCHEDULE_CLAIM_LEASE_MS,
MIN_CLUSTER_SCHEDULE_CLAIM_LEASE_MS,
resolveClusterScheduleDecision,
type ClusterScheduleStore,
} from '@qinglong/runtime-core/cluster-scheduler';
import type { LocalCronNextOccurrence } from '@qinglong/runtime-core/local-scheduler';
import { cronerClusterNextOccurrence } from './cronerSchedule';
export const MAX_CLUSTER_SCHEDULE_CLAIMS_PER_CYCLE = 256;
export interface ClusterSchedulerCoordinatorOptions {
readonly ownerId: string;
readonly claimLeaseMs?: number;
readonly maxClaimsPerCycle?: number;
readonly misfireGraceMs?: number;
readonly createId?: () => string;
readonly nextOccurrence?: LocalCronNextOccurrence;
readonly onAdmitted?: (
runId: string,
attemptId: string,
) => void | Promise<void>;
}
export interface ClusterSchedulerCycleSummary {
readonly firstClaimAcquiredAtMs: number | null;
readonly lastClaimAcquiredAtMs: number | null;
readonly claimed: number;
readonly initialized: number;
readonly skipped: number;
readonly admitted: number;
readonly raced: number;
readonly saturated: boolean;
}
const OWNER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const COORDINATOR_OPTION_KEYS = new Set([
'claimLeaseMs',
'createId',
'maxClaimsPerCycle',
'misfireGraceMs',
'nextOccurrence',
'onAdmitted',
'ownerId',
]);
export class ClusterSchedulerCoordinator {
private readonly ownerId: string;
private readonly claimLeaseMs: number;
private readonly maxClaimsPerCycle: number;
private readonly misfireGraceMs: number;
private readonly createId: () => string;
private readonly nextOccurrence: LocalCronNextOccurrence;
private readonly onAdmitted?: ClusterSchedulerCoordinatorOptions['onAdmitted'];
constructor(
private readonly schedules: ClusterScheduleStore,
options: ClusterSchedulerCoordinatorOptions,
) {
this.ownerId = options?.ownerId ?? '';
this.claimLeaseMs = options?.claimLeaseMs ?? 30_000;
this.maxClaimsPerCycle = options?.maxClaimsPerCycle ?? 16;
this.misfireGraceMs = options?.misfireGraceMs ?? 30_000;
this.createId = options?.createId ?? randomUUID;
this.nextOccurrence =
options?.nextOccurrence ?? cronerClusterNextOccurrence;
this.onAdmitted = options?.onAdmitted;
if (
!schedules ||
typeof schedules.claimNextClusterSchedule !== 'function' ||
typeof schedules.commitClusterScheduleDecision !== 'function' ||
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
Object.keys(options).some((key) => !COORDINATOR_OPTION_KEYS.has(key)) ||
!OWNER_PATTERN.test(this.ownerId) ||
!Number.isSafeInteger(this.claimLeaseMs) ||
this.claimLeaseMs < MIN_CLUSTER_SCHEDULE_CLAIM_LEASE_MS ||
this.claimLeaseMs > MAX_CLUSTER_SCHEDULE_CLAIM_LEASE_MS ||
!Number.isSafeInteger(this.maxClaimsPerCycle) ||
this.maxClaimsPerCycle < 1 ||
this.maxClaimsPerCycle > MAX_CLUSTER_SCHEDULE_CLAIMS_PER_CYCLE ||
!Number.isSafeInteger(this.misfireGraceMs) ||
this.misfireGraceMs < 0 ||
this.misfireGraceMs > 5 * 60_000 ||
typeof this.createId !== 'function' ||
typeof this.nextOccurrence !== 'function' ||
(this.onAdmitted !== undefined && typeof this.onAdmitted !== 'function')
) {
throw new TypeError('Cluster scheduler coordinator options are invalid');
}
}
async scheduleOnce(): Promise<ClusterSchedulerCycleSummary> {
const stats: {
firstClaimAcquiredAtMs: number | null;
lastClaimAcquiredAtMs: number | null;
claimed: number;
initialized: number;
skipped: number;
admitted: number;
raced: number;
saturated: boolean;
} = {
firstClaimAcquiredAtMs: null,
lastClaimAcquiredAtMs: null,
claimed: 0,
initialized: 0,
skipped: 0,
admitted: 0,
raced: 0,
saturated: false,
};
while (stats.claimed < this.maxClaimsPerCycle) {
const claimToken = this.createId();
const claimed = await this.schedules.claimNextClusterSchedule({
ownerId: this.ownerId,
claimToken,
leaseMs: this.claimLeaseMs,
});
if (!claimed) break;
if (
claimed.claimOwner !== this.ownerId ||
claimed.claimToken !== claimToken ||
claimed.claimExpiresAtMs !==
claimed.claimAcquiredAtMs + this.claimLeaseMs
) {
throw new TypeError('Cluster scheduler store returned a foreign claim');
}
stats.claimed += 1;
stats.firstClaimAcquiredAtMs ??= claimed.claimAcquiredAtMs;
stats.lastClaimAcquiredAtMs = claimed.claimAcquiredAtMs;
const decision = resolveClusterScheduleDecision(
claimed,
this.misfireGraceMs,
this.nextOccurrence,
);
const admitted = decision.disposition === 'admit';
const result = await this.schedules.commitClusterScheduleDecision({
claim: claimed,
decision,
...(admitted
? {
runId: this.createId(),
attemptId: this.createId(),
createdEventId: this.createId(),
queuedEventId: this.createId(),
}
: {}),
});
if (result.status === 'raced') {
stats.raced += 1;
continue;
}
if (result.disposition === 'initialize') stats.initialized += 1;
if (result.disposition === 'skip') stats.skipped += 1;
if (result.status === 'admitted') {
stats.admitted += 1;
await this.onAdmitted?.(result.runId, result.attemptId);
}
}
stats.saturated = stats.claimed === this.maxClaimsPerCycle;
return Object.freeze(stats);
}
}
export interface ClusterSchedulerLifecycleOptions {
readonly intervalMs: number;
readonly stopTimeoutMs: number;
readonly onDiagnostic?: (
error: unknown,
summary?: ClusterSchedulerCycleSummary,
) => void | Promise<void>;
}
export interface ClusterSchedulerLifecycleStopSummary {
readonly status: 'stopped' | 'timed_out';
}
export class ClusterSchedulerLifecycle {
private timer: NodeJS.Timeout | undefined;
private inFlight: Promise<ClusterSchedulerCycleSummary> | undefined;
private stopPromise:
| Promise<ClusterSchedulerLifecycleStopSummary>
| undefined;
private running = false;
private stopping = false;
constructor(
private readonly scheduler: Pick<
ClusterSchedulerCoordinator,
'scheduleOnce'
>,
private readonly options: ClusterSchedulerLifecycleOptions,
) {
if (
!scheduler ||
typeof scheduler.scheduleOnce !== 'function' ||
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
!Number.isSafeInteger(options.intervalMs) ||
options.intervalMs < 250 ||
options.intervalMs > 60 * 60_000 ||
!Number.isSafeInteger(options.stopTimeoutMs) ||
options.stopTimeoutMs < 100 ||
options.stopTimeoutMs > 30_000 ||
(options.onDiagnostic !== undefined &&
typeof options.onDiagnostic !== 'function')
) {
throw new TypeError('Cluster scheduler lifecycle options are invalid');
}
}
start(): 'started' {
if (!this.running && !this.stopping) {
this.running = true;
this.schedule();
}
return 'started';
}
runOnce(): Promise<ClusterSchedulerCycleSummary> {
if (this.stopping) {
return Promise.reject(
new Error('Cluster scheduler lifecycle is stopping'),
);
}
if (this.inFlight) return this.inFlight;
const work = this.scheduler.scheduleOnce().finally(() => {
if (this.inFlight === work) this.inFlight = undefined;
});
this.inFlight = work;
return work;
}
stopAndDrain(): Promise<ClusterSchedulerLifecycleStopSummary> {
if (this.stopPromise) return this.stopPromise;
this.stopping = true;
this.running = false;
if (this.timer) clearTimeout(this.timer);
this.timer = undefined;
this.stopPromise = (async () => {
const work = this.inFlight;
if (!work) return Object.freeze({ status: 'stopped' as const });
let timeout: NodeJS.Timeout | undefined;
try {
return await Promise.race([
work.then(
() => Object.freeze({ status: 'stopped' as const }),
() => Object.freeze({ status: 'stopped' as const }),
),
new Promise<ClusterSchedulerLifecycleStopSummary>((resolve) => {
timeout = setTimeout(
() => resolve(Object.freeze({ status: 'timed_out' as const })),
this.options.stopTimeoutMs,
);
timeout.unref?.();
}),
]);
} finally {
if (timeout) clearTimeout(timeout);
}
})();
return this.stopPromise;
}
private schedule(): void {
if (!this.running || this.timer) return;
this.timer = setTimeout(() => {
this.timer = undefined;
if (!this.running) return;
void this.runOnce()
.then((summary) => this.diagnostic(undefined, summary))
.catch((error) => this.diagnostic(error))
.finally(() => this.schedule());
}, this.options.intervalMs);
this.timer.unref?.();
}
private async diagnostic(
error: unknown,
summary?: ClusterSchedulerCycleSummary,
): Promise<void> {
if (this.stopping) return;
try {
await this.options.onDiagnostic?.(error, summary);
} catch {
// Diagnostics cannot own or stop scheduling.
}
}
}
@@ -0,0 +1,252 @@
// Scheduling owns Workflow frontier and Task Attempt admission on the shared cadence.
import type {
PluginPackageWorkflowFrontierCursor,
PluginPackageWorkflowFrontierRepository,
} from '@qinglong/runtime-core/plugin-package-workflow-frontier';
import type {
PluginPackageWorkflowTaskAttemptAdmissionCursor,
PluginPackageWorkflowTaskAttemptAdmissionRepository,
} from '@qinglong/runtime-core/plugin-package-workflow-task-attempt-admission';
import type {
ClusterSchedulerCoordinator,
ClusterSchedulerCycleSummary,
} from './scheduler';
export interface ClusterWorkflowSchedulerOptions {
readonly frontierPageSize: number;
readonly frontierMaxPages: number;
readonly taskAttemptPageSize: number;
readonly taskAttemptMaxPages: number;
}
export interface ClusterWorkflowSchedulerCycleSummary {
readonly frontierPages: number;
readonly frontierScanned: number;
readonly frontierAdvanced: number;
readonly frontierTruncated: boolean;
readonly taskAttemptPages: number;
readonly taskAttemptsScanned: number;
readonly taskAttemptsCreated: number;
readonly taskAttemptsExisting: number;
readonly taskAttemptsTruncated: boolean;
}
function bounded(
label: string,
value: number,
maximum: number,
): number {
if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {
throw new RangeError(`${label} must be between 1 and ${maximum}`);
}
return value;
}
function nextFrontierCursor(
current: PluginPackageWorkflowFrontierCursor | undefined,
next: PluginPackageWorkflowFrontierCursor | undefined,
): PluginPackageWorkflowFrontierCursor {
if (
!next ||
(current !== undefined &&
(next.admittedAtMs < current.admittedAtMs ||
(next.admittedAtMs === current.admittedAtMs &&
next.planDigest <= current.planDigest)))
) {
throw new TypeError(
'Cluster Workflow frontier continuation did not advance',
);
}
return next;
}
function nextTaskAttemptCursor(
current: PluginPackageWorkflowTaskAttemptAdmissionCursor | undefined,
next: PluginPackageWorkflowTaskAttemptAdmissionCursor | undefined,
): PluginPackageWorkflowTaskAttemptAdmissionCursor {
if (
!next ||
(current !== undefined &&
(next.readyAtMs < current.readyAtMs ||
(next.readyAtMs === current.readyAtMs &&
next.stepRunId <= current.stepRunId)))
) {
throw new TypeError(
'Cluster Workflow Task Attempt continuation did not advance',
);
}
return next;
}
/**
* Extends the existing Cluster Scheduler cadence with Workflow frontier and
* Task Attempt admission. It owns no timer, connection, watcher, or
* per-Workflow state.
*/
export class ClusterWorkflowSchedulerCoordinator {
private readonly frontierPageSize: number;
private readonly frontierMaxPages: number;
private readonly taskAttemptPageSize: number;
private readonly taskAttemptMaxPages: number;
private inFlight: Promise<ClusterSchedulerCycleSummary> | undefined;
private latestWorkflow:
| Readonly<ClusterWorkflowSchedulerCycleSummary>
| undefined;
constructor(
private readonly scheduler: Pick<
ClusterSchedulerCoordinator,
'scheduleOnce'
>,
private readonly frontier: PluginPackageWorkflowFrontierRepository,
private readonly taskAttempts: PluginPackageWorkflowTaskAttemptAdmissionRepository,
options: ClusterWorkflowSchedulerOptions,
) {
if (
typeof scheduler?.scheduleOnce !== 'function' ||
typeof frontier?.listCandidates !== 'function' ||
typeof frontier?.advance !== 'function' ||
typeof taskAttempts?.listCandidates !== 'function' ||
typeof taskAttempts?.admit !== 'function' ||
!options ||
typeof options !== 'object' ||
Array.isArray(options)
) {
throw new TypeError('Cluster Workflow scheduler is invalid');
}
this.frontierPageSize = bounded(
'Cluster Workflow frontier page size',
options.frontierPageSize,
64,
);
this.frontierMaxPages = bounded(
'Cluster Workflow frontier page limit',
options.frontierMaxPages,
16,
);
this.taskAttemptPageSize = bounded(
'Cluster Workflow Task Attempt page size',
options.taskAttemptPageSize,
64,
);
this.taskAttemptMaxPages = bounded(
'Cluster Workflow Task Attempt page limit',
options.taskAttemptMaxPages,
16,
);
}
scheduleOnce(): Promise<ClusterSchedulerCycleSummary> {
if (this.inFlight) return this.inFlight;
const work = this.runCycle().finally(() => {
if (this.inFlight === work) this.inFlight = undefined;
});
this.inFlight = work;
return work;
}
latestWorkflowSummary():
| Readonly<ClusterWorkflowSchedulerCycleSummary>
| undefined {
return this.latestWorkflow;
}
private async runCycle(): Promise<ClusterSchedulerCycleSummary> {
const scheduler = await this.scheduler.scheduleOnce();
const frontier = await this.advanceFrontier();
const taskAttempts = await this.admitTaskAttempts();
this.latestWorkflow = Object.freeze({
...frontier,
...taskAttempts,
});
return scheduler;
}
private async advanceFrontier(): Promise<Readonly<{
frontierPages: number;
frontierScanned: number;
frontierAdvanced: number;
frontierTruncated: boolean;
}>> {
let frontierPages = 0;
let frontierScanned = 0;
let frontierAdvanced = 0;
let frontierTruncated = false;
let after: PluginPackageWorkflowFrontierCursor | undefined;
for (let index = 0; index < this.frontierMaxPages; index += 1) {
const page = await this.frontier.listCandidates({
limit: this.frontierPageSize,
...(after === undefined ? {} : { after }),
});
if (page.candidates.length > this.frontierPageSize) {
throw new RangeError(
'Cluster Workflow frontier exceeded its page size',
);
}
frontierPages += 1;
frontierScanned += page.candidates.length;
for (const candidate of page.candidates) {
await this.frontier.advance(candidate.runId);
frontierAdvanced += 1;
}
frontierTruncated = page.truncated;
if (!page.truncated) break;
after = nextFrontierCursor(after, page.next);
}
return Object.freeze({
frontierPages,
frontierScanned,
frontierAdvanced,
frontierTruncated,
});
}
private async admitTaskAttempts(): Promise<Readonly<{
taskAttemptPages: number;
taskAttemptsScanned: number;
taskAttemptsCreated: number;
taskAttemptsExisting: number;
taskAttemptsTruncated: boolean;
}>> {
let taskAttemptPages = 0;
let taskAttemptsScanned = 0;
let taskAttemptsCreated = 0;
let taskAttemptsExisting = 0;
let taskAttemptsTruncated = false;
let after:
| PluginPackageWorkflowTaskAttemptAdmissionCursor
| undefined;
for (let index = 0; index < this.taskAttemptMaxPages; index += 1) {
const page = await this.taskAttempts.listCandidates({
limit: this.taskAttemptPageSize,
...(after === undefined ? {} : { after }),
});
if (page.candidates.length > this.taskAttemptPageSize) {
throw new RangeError(
'Cluster Workflow Task Attempt source exceeded its page size',
);
}
taskAttemptPages += 1;
taskAttemptsScanned += page.candidates.length;
for (const candidate of page.candidates) {
const admitted = await this.taskAttempts.admit(
candidate.runId,
candidate.stepRunId,
);
if (admitted.status === 'created') taskAttemptsCreated += 1;
else taskAttemptsExisting += 1;
}
taskAttemptsTruncated = page.truncated;
if (!page.truncated) break;
after = nextTaskAttemptCursor(after, page.next);
}
return Object.freeze({
taskAttemptPages,
taskAttemptsScanned,
taskAttemptsCreated,
taskAttemptsExisting,
taskAttemptsTruncated,
});
}
}
@@ -0,0 +1,116 @@
import {
BoundedTaskListProjectionUnavailableError,
InvalidBoundedTaskListProjectionError,
executeBoundedTaskListProjection,
} from '@qinglong/runtime-core/bounded-task-list-projection';
import type { TaskDefinitionSource } from '@qinglong/runtime-core/task-definition';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
} from '../transport/routeRegistry';
export const CLUSTER_CONTROL_TASK_LIST_ROUTE = Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/tasks',
operationId: 'task.list',
permission: 'task.read',
projectParameter: 'projectId',
allowedQuery: Object.freeze(['after_task_id', 'limit']),
});
const TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function parseQuery(
query: Readonly<Record<string, readonly string[]>>,
): Readonly<{
limit?: number;
after?: Readonly<{ taskId: string }>;
}> {
const limitValues = query.limit;
const taskIdValues = query.after_task_id;
if (
(limitValues !== undefined && limitValues.length !== 1) ||
(taskIdValues !== undefined && taskIdValues.length !== 1)
) {
throw new TypeError();
}
const rawLimit = limitValues?.[0];
const limit = rawLimit === undefined ? undefined : Number(rawLimit);
const taskId = taskIdValues?.[0];
if (
(rawLimit !== undefined &&
(!Number.isSafeInteger(limit) ||
Number(limit) < 1 ||
Number(limit) > 64 ||
String(limit) !== rawLimit)) ||
(taskId !== undefined && !TASK_ID_PATTERN.test(taskId))
) {
throw new TypeError();
}
return Object.freeze({
...(limit === undefined ? {} : { limit }),
...(taskId === undefined
? {}
: { after: Object.freeze({ taskId }) }),
});
}
function validateTaskListQuery(
query: Readonly<Record<string, readonly string[]>>,
): void {
parseQuery(query);
}
export function createClusterControlTaskListRoute(
tasks: Pick<TaskDefinitionSource, 'listTaskDefinitions'>,
): Readonly<ClusterControlRouteDefinition> {
if (!tasks || typeof tasks.listTaskDefinitions !== 'function') {
throw new TypeError('Cluster-control Task list repository is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_TASK_LIST_ROUTE,
validateQuery: validateTaskListQuery,
async handle(authorized: ClusterControlAuthorizedOperationRequest) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
if (authorized.projectId === null) {
return response(503, { code: 'task_list_unavailable' });
}
let input;
try {
input = parseQuery(authorized.request.query);
} catch {
return response(400, { code: 'invalid_task_list_query' });
}
try {
const result = await executeBoundedTaskListProjection(
tasks,
authorized.projectId,
input,
);
return response(200, { ...result });
} catch (error) {
if (
error instanceof InvalidBoundedTaskListProjectionError ||
error instanceof BoundedTaskListProjectionUnavailableError
) {
return response(503, { code: 'task_list_unavailable' });
}
throw error;
}
},
});
}
export * from './taskReadRoute';
export * from './taskStartRoute';
@@ -0,0 +1,70 @@
import {
BoundedTaskReadProjectionUnavailableError,
InvalidBoundedTaskReadProjectionError,
executeBoundedTaskReadProjection,
} from '@qinglong/runtime-core/bounded-task-read-projection';
import type { TaskDefinitionSource } from '@qinglong/runtime-core/task-definition';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../transport/routeRegistry';
export const CLUSTER_CONTROL_TASK_READ_ROUTE = Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/tasks/{taskId}',
operationId: 'task.get',
permission: 'task.read',
projectParameter: 'projectId',
});
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
export function createClusterControlTaskReadRoute(
tasks: Pick<TaskDefinitionSource, 'findCurrentTaskDefinition'>,
): Readonly<ClusterControlRouteDefinition> {
if (!tasks || typeof tasks.findCurrentTaskDefinition !== 'function') {
throw new TypeError('Cluster-control Task read repository is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_TASK_READ_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
if (authorized.request.body !== null) {
return response(400, { code: 'invalid_request_body' });
}
if (authorized.projectId === null) {
return response(503, { code: 'task_query_unavailable' });
}
try {
const projection = await executeBoundedTaskReadProjection(
tasks,
authorized.projectId,
parameters.taskId!,
);
if (projection.found !== true) {
return response(404, { code: 'task_not_found' });
}
const { found: _found, ...task } = projection;
return response(200, { task: Object.freeze(task) });
} catch (error) {
if (
error instanceof InvalidBoundedTaskReadProjectionError ||
error instanceof BoundedTaskReadProjectionUnavailableError
) {
return response(503, { code: 'task_query_unavailable' });
}
throw error;
}
},
});
}
@@ -0,0 +1,114 @@
import {
TASK_START_SCHEMA,
InvalidTaskStartError,
TaskStartFenceRejectedError,
TaskStartNotFoundError,
TaskStartUnavailableError,
createTaskStartResponseBody,
parseTaskStartRequestBody,
type TaskStartRepository,
} from '@qinglong/runtime-core/task-start';
import type { ClusterControlAdmissionResponse } from '../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../transport/routeRegistry';
export const CLUSTER_CONTROL_TASK_START_ROUTE = Object.freeze({
method: 'POST' as const,
path: '/api/v3/projects/{projectId}/tasks/{taskId}/runs',
operationId: 'task.start',
permission: 'run.start',
projectParameter: 'projectId',
});
export type ClusterTaskStartIdFactory = () => string;
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
export function createClusterControlTaskStartRoute(
repository: TaskStartRepository,
createId: ClusterTaskStartIdFactory,
): Readonly<ClusterControlRouteDefinition> {
if (
!repository ||
typeof repository.startTask !== 'function' ||
typeof createId !== 'function'
) {
throw new TypeError('Cluster-control Task start route is invalid');
}
return Object.freeze({
...CLUSTER_CONTROL_TASK_START_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
let body;
try {
body = parseTaskStartRequestBody(authorized.request.body);
} catch (error) {
if (error instanceof InvalidTaskStartError) {
return response(400, {
code: 'invalid_task_start_request',
schema: TASK_START_SCHEMA,
});
}
return response(503, { code: 'task_start_unavailable' });
}
const projectId = authorized.projectId;
const taskId = parameters.taskId;
if (
projectId === null ||
typeof taskId !== 'string' ||
taskId.length < 1 ||
!authorized.policyFence ||
authorized.policyFence.bindingVersion === null
) {
return response(503, { code: 'task_start_unavailable' });
}
try {
const result = await repository.startTask({
projectId,
taskId,
mutationId: body.mutationId,
expectedRevision: body.expectedRevision,
expectedContentDigest: body.expectedContentDigest,
runId: createId(),
attemptId: createId(),
createdEventId: createId(),
queuedEventId: createId(),
subject: authorized.principal.subject,
policyFence: authorized.policyFence,
});
return response(
result.status === 'accepted' ? 202 : 200,
createTaskStartResponseBody(result),
);
} catch (error) {
if (error instanceof TaskStartNotFoundError) {
return response(404, { code: 'task_not_found' });
}
if (error instanceof TaskStartFenceRejectedError) {
return response(409, {
code: 'task_start_fence_rejected',
reason: error.reason,
});
}
if (
error instanceof InvalidTaskStartError ||
error instanceof TaskStartUnavailableError
) {
return response(503, { code: 'task_start_unavailable' });
}
return response(503, { code: 'task_start_unavailable' });
}
},
});
}
@@ -0,0 +1,341 @@
// Transport owns authenticated, Policy-fenced and synchronously audited admission.
import { randomUUID } from 'node:crypto';
import {
normalizeSecurityPolicyDecision,
normalizeSecurityPrincipal,
type SecurityPolicyDecision,
type SecurityPrincipal,
} from '@qinglong/runtime-core/security';
import {
normalizeSecurityAuditRecord,
type SecurityAuditOutcome,
type SecurityAuditRecord,
type SecurityAuditSink,
} from '@qinglong/runtime-core/security-audit';
import {
ProjectPolicyEngine,
normalizeProjectPermission,
type ProjectPolicyRepository,
} from '@qinglong/runtime-core/project-policy';
import type {
ClusterControlAdmissionMetadata,
ClusterControlAdmissionPipeline,
} from './httpSurface';
import {
ClusterControlRouteResolutionError,
isClusterControlRouteRegistry,
type ClusterControlAuthorizedOperationRequest,
type ClusterControlRoute,
type ClusterControlRouteRegistry,
} from './routeRegistry';
export type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRoute,
ClusterControlRouteRegistry,
} from './routeRegistry';
export interface ClusterControlRequestAuthenticator {
authenticate(
request: ClusterControlAdmissionMetadata,
): SecurityPrincipal | null | Promise<SecurityPrincipal | null>;
}
export interface ClusterControlPolicyRequest {
readonly principal: Readonly<SecurityPrincipal>;
readonly operationId: string;
readonly permission: string;
readonly projectId: string | null;
readonly signal: AbortSignal;
}
export interface ClusterControlPolicyAuthorizer {
authorize(
request: ClusterControlPolicyRequest,
): SecurityPolicyDecision | Promise<SecurityPolicyDecision>;
}
export type ClusterControlSecurityAuditOutcome = SecurityAuditOutcome;
export type ClusterControlSecurityAuditRecord = SecurityAuditRecord;
export type ClusterControlSecurityAuditSink = SecurityAuditSink;
export interface ClusterControlAdmissionPipelineOptions {
readonly routes: ClusterControlRouteRegistry;
readonly authenticator: ClusterControlRequestAuthenticator;
readonly policy: ClusterControlPolicyAuthorizer;
readonly audit: ClusterControlSecurityAuditSink;
readonly now?: () => number;
}
export class ClusterControlAdmissionSecurityError extends Error {
constructor(
readonly statusCode: number,
readonly code: string,
message: string,
) {
super(message);
this.name = 'ClusterControlAdmissionSecurityError';
}
}
/** Adapts the shared Project Policy engine to cluster admission. */
export function createClusterControlProjectPolicyAuthorizer(
repository: ProjectPolicyRepository,
): ClusterControlPolicyAuthorizer {
const engine = new ProjectPolicyEngine(repository);
return Object.freeze({
authorize(request: ClusterControlPolicyRequest) {
if (request.projectId === null) {
return Object.freeze({
effect: 'deny' as const,
reasons: Object.freeze(['project_scope_required']),
fence: null,
});
}
return engine.authorize(
request.principal,
request.projectId,
normalizeProjectPermission(request.permission),
);
},
});
}
function securityError(
statusCode: number,
code: string,
message: string,
): ClusterControlAdmissionSecurityError {
return new ClusterControlAdmissionSecurityError(statusCode, code, message);
}
async function recordSecurityAudit(
audit: ClusterControlSecurityAuditSink,
record: Omit<SecurityAuditRecord, 'eventId' | 'occurredAtMs'>,
now: () => number,
): Promise<void> {
try {
await audit.record(
normalizeSecurityAuditRecord({
...record,
eventId: randomUUID(),
occurredAtMs: now(),
}),
);
} catch {
throw securityError(
503,
'security_audit_unavailable',
'Cluster-control security audit is unavailable',
);
}
}
/**
* Creates a fail-closed, two-phase admission pipeline. Route matching,
* authentication, policy evaluation and durable security audit all complete
* before the returned operation is allowed to receive a request body.
*/
export function createClusterControlAdmissionPipeline(
options: ClusterControlAdmissionPipelineOptions,
): ClusterControlAdmissionPipeline {
if (
!options ||
typeof options !== 'object' ||
!isClusterControlRouteRegistry(options.routes) ||
typeof options.authenticator?.authenticate !== 'function' ||
typeof options.policy?.authorize !== 'function' ||
typeof options.audit?.record !== 'function' ||
(options.now !== undefined && typeof options.now !== 'function')
) {
throw new TypeError(
'Cluster-control admission pipeline options are invalid',
);
}
const now = options.now ?? Date.now;
return Object.freeze({
async prepare(request: ClusterControlAdmissionMetadata) {
let route: ClusterControlRoute;
try {
const resolved = await options.routes.resolve(request);
if (!resolved) {
throw securityError(
404,
'route_not_found',
'Cluster-control route was not found',
);
}
route = resolved;
} catch (error) {
if (
error instanceof ClusterControlAdmissionSecurityError ||
error instanceof ClusterControlRouteResolutionError
) {
throw error;
}
throw securityError(
503,
'route_resolution_unavailable',
'Cluster-control route resolution is unavailable',
);
}
let candidate: SecurityPrincipal | null;
try {
candidate = await options.authenticator.authenticate(request);
} catch {
await recordSecurityAudit(
options.audit,
{
requestId: request.requestId,
operationId: route.operationId,
projectId: route.projectId,
subject: null,
authenticationId: null,
outcome: 'authentication_unavailable',
reasons: Object.freeze(['authentication_unavailable']),
fence: null,
},
now,
);
throw securityError(
503,
'authentication_unavailable',
'Cluster-control authentication is unavailable',
);
}
if (!candidate) {
await recordSecurityAudit(
options.audit,
{
requestId: request.requestId,
operationId: route.operationId,
projectId: route.projectId,
subject: null,
authenticationId: null,
outcome: 'authentication_rejected',
reasons: Object.freeze(['authentication_rejected']),
fence: null,
},
now,
);
throw securityError(
401,
'authentication_required',
'Cluster-control authentication is required',
);
}
let principal: Readonly<SecurityPrincipal>;
try {
principal = normalizeSecurityPrincipal(candidate, now());
} catch {
await recordSecurityAudit(
options.audit,
{
requestId: request.requestId,
operationId: route.operationId,
projectId: route.projectId,
subject: null,
authenticationId: null,
outcome: 'authentication_unavailable',
reasons: Object.freeze(['invalid_principal']),
fence: null,
},
now,
);
throw securityError(
503,
'authentication_unavailable',
'Cluster-control authentication is unavailable',
);
}
let decision: Readonly<SecurityPolicyDecision>;
try {
decision = normalizeSecurityPolicyDecision(
await options.policy.authorize(
Object.freeze({
principal,
operationId: route.operationId,
permission: route.permission,
projectId: route.projectId,
signal: request.signal,
}),
),
);
} catch {
await recordSecurityAudit(
options.audit,
{
requestId: request.requestId,
operationId: route.operationId,
projectId: route.projectId,
subject: principal.subject,
authenticationId: principal.authenticationId,
outcome: 'authorization_unavailable',
reasons: Object.freeze(['authorization_unavailable']),
fence: null,
},
now,
);
throw securityError(
503,
'authorization_unavailable',
'Cluster-control authorization is unavailable',
);
}
const outcome =
decision.effect === 'allow'
? 'allowed'
: decision.effect === 'require_approval'
? 'approval_required'
: 'denied';
await recordSecurityAudit(
options.audit,
{
requestId: request.requestId,
operationId: route.operationId,
projectId: route.projectId,
subject: principal.subject,
authenticationId: principal.authenticationId,
outcome,
reasons: decision.reasons,
fence: decision.fence,
},
now,
);
if (decision.effect === 'deny') {
throw securityError(
403,
'forbidden',
'Cluster-control operation is forbidden',
);
}
if (decision.effect === 'require_approval') {
throw securityError(
403,
'approval_required',
'Cluster-control operation requires approval',
);
}
return Object.freeze({
handle(body: unknown | null) {
return route.handle(
Object.freeze({
request: Object.freeze({ ...request, body }),
principal,
operationId: route.operationId,
permission: route.permission,
projectId: route.projectId,
policyFence: decision.fence,
}),
);
},
});
},
});
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,452 @@
// Transport owns bounded route compilation, resolution and query validation.
import type {
SecurityPolicyFence,
SecurityPrincipal,
} from '@qinglong/runtime-core/security';
import type {
ClusterControlAdmissionMetadata,
ClusterControlAdmissionRequest,
ClusterControlAdmissionResponse,
ClusterControlHttpMethod,
} from './httpSurface';
export const CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS = Object.freeze({
maxRoutes: 256,
maxPathBytes: 1024,
maxPathSegments: 16,
maxPathParameters: 8,
maxQueryParameters: 16,
maxQueryValuesPerParameter: 16,
maxQueryValueBytes: 1024,
});
export type ClusterControlRouteParameters = Readonly<Record<string, string>>;
export interface ClusterControlAuthorizedOperationRequest {
readonly request: ClusterControlAdmissionRequest;
readonly principal: Readonly<SecurityPrincipal>;
readonly operationId: string;
readonly permission: string;
readonly projectId: string | null;
readonly policyFence: Readonly<SecurityPolicyFence> | null;
}
export interface ClusterControlRouteDefinition {
readonly method: ClusterControlHttpMethod;
readonly path: string;
readonly operationId: string;
readonly permission: string;
readonly projectParameter: string | null;
readonly allowedQuery?: readonly string[];
readonly validateQuery?: (
query: Readonly<Record<string, readonly string[]>>,
) => void;
handle(
request: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
): ClusterControlAdmissionResponse | Promise<ClusterControlAdmissionResponse>;
}
export interface ClusterControlRoute {
readonly operationId: string;
readonly permission: string;
readonly projectId: string | null;
handle(
request: ClusterControlAuthorizedOperationRequest,
): ClusterControlAdmissionResponse | Promise<ClusterControlAdmissionResponse>;
}
export interface ClusterControlRouteResolver {
resolve(request: ClusterControlAdmissionMetadata): ClusterControlRoute | null;
}
export interface ClusterControlRouteRegistry
extends ClusterControlRouteResolver {
readonly contractVersion: 1;
readonly size: number;
}
export class ClusterControlRouteRegistryConfigurationError extends TypeError {
constructor(message: string) {
super(`Cluster-control route registry is invalid: ${message}`);
this.name = 'ClusterControlRouteRegistryConfigurationError';
}
}
export class ClusterControlRouteResolutionError extends Error {
constructor(
readonly statusCode: 400,
readonly code: 'invalid_route_path' | 'invalid_route_query',
message: string,
) {
super(message);
this.name = 'ClusterControlRouteResolutionError';
}
}
type CompiledSegment =
| { readonly kind: 'literal'; readonly value: string }
| { readonly kind: 'parameter'; readonly name: string };
interface CompiledRoute {
readonly method: ClusterControlHttpMethod;
readonly operationId: string;
readonly permission: string;
readonly projectParameter: string | null;
readonly segments: readonly CompiledSegment[];
readonly allowedQuery: ReadonlySet<string>;
readonly validateQuery?: ClusterControlRouteDefinition['validateQuery'];
readonly handle: ClusterControlRouteDefinition['handle'];
}
const HTTP_METHODS = new Set<ClusterControlHttpMethod>([
'DELETE',
'GET',
'PATCH',
'POST',
'PUT',
]);
const OPERATION_PATTERN = /^[a-z][a-z0-9_.:-]{0,127}$/;
const PERMISSION_PATTERN = /^[a-z][a-z0-9_.:*:-]{0,127}$/;
const LITERAL_SEGMENT_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
const PARAMETER_NAME_PATTERN = /^[a-z][A-Za-z0-9]{0,63}$/;
const PARAMETER_SEGMENT_PATTERN = /^\{([a-z][A-Za-z0-9]{0,63})\}$/;
const PARAMETER_VALUE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const QUERY_NAME_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/;
const DEFINITION_KEYS = new Set([
'allowedQuery',
'handle',
'method',
'operationId',
'path',
'permission',
'projectParameter',
'validateQuery',
]);
const reviewedRegistries = new WeakSet<object>();
function configurationError(
message: string,
): ClusterControlRouteRegistryConfigurationError {
return new ClusterControlRouteRegistryConfigurationError(message);
}
function exactDefinitionShape(value: ClusterControlRouteDefinition): void {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw configurationError('each route must be an object');
}
const keys = Object.keys(value);
if (
keys.some((key) => !DEFINITION_KEYS.has(key)) ||
!keys.includes('method') ||
!keys.includes('path') ||
!keys.includes('operationId') ||
!keys.includes('permission') ||
!keys.includes('projectParameter') ||
!keys.includes('handle')
) {
throw configurationError('route shape is invalid');
}
}
function compilePath(path: string): readonly CompiledSegment[] {
if (
typeof path !== 'string' ||
Buffer.byteLength(path) >
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxPathBytes ||
!path.startsWith('/api/v3/') ||
path.endsWith('/') ||
path.includes('//') ||
path.includes('%') ||
path.includes('\\') ||
CONTROL_CHARACTER_PATTERN.test(path)
) {
throw configurationError('route path must be a canonical /api/v3 path');
}
const rawSegments = path.slice(1).split('/');
if (
rawSegments.length > CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxPathSegments
) {
throw configurationError('route path has too many segments');
}
const parameterNames = new Set<string>();
const compiled = rawSegments.map((segment): CompiledSegment => {
const parameter = PARAMETER_SEGMENT_PATTERN.exec(segment)?.[1];
if (parameter) {
if (parameterNames.has(parameter)) {
throw configurationError('route path repeats a parameter');
}
parameterNames.add(parameter);
return Object.freeze({ kind: 'parameter', name: parameter });
}
if (!LITERAL_SEGMENT_PATTERN.test(segment)) {
throw configurationError('route path contains an invalid segment');
}
return Object.freeze({ kind: 'literal', value: segment });
});
if (
parameterNames.size >
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxPathParameters
) {
throw configurationError('route path has too many parameters');
}
return Object.freeze(compiled);
}
function compileAllowedQuery(
value: readonly string[] | undefined,
): ReadonlySet<string> {
if (value === undefined) return new Set<string>();
if (
!Array.isArray(value) ||
value.length > CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxQueryParameters
) {
throw configurationError('allowedQuery is invalid');
}
const names = new Set<string>();
for (const name of value) {
if (!QUERY_NAME_PATTERN.test(name) || names.has(name)) {
throw configurationError('allowedQuery contains an invalid name');
}
names.add(name);
}
return names;
}
function compileRoute(
definition: ClusterControlRouteDefinition,
): CompiledRoute {
exactDefinitionShape(definition);
if (!HTTP_METHODS.has(definition.method)) {
throw configurationError('route method is invalid');
}
if (!OPERATION_PATTERN.test(definition.operationId)) {
throw configurationError('route operationId is invalid');
}
if (!PERMISSION_PATTERN.test(definition.permission)) {
throw configurationError('route permission is invalid');
}
if (typeof definition.handle !== 'function') {
throw configurationError('route handler is invalid');
}
if (
definition.validateQuery !== undefined &&
typeof definition.validateQuery !== 'function'
) {
throw configurationError('route query validator is invalid');
}
const segments = compilePath(definition.path);
const parameterNames = new Set(
segments.flatMap((segment) =>
segment.kind === 'parameter' ? [segment.name] : [],
),
);
if (
definition.projectParameter !== null &&
(!PARAMETER_NAME_PATTERN.test(definition.projectParameter) ||
!parameterNames.has(definition.projectParameter))
) {
throw configurationError(
'projectParameter must name one declared path parameter',
);
}
return Object.freeze({
method: definition.method,
operationId: definition.operationId,
permission: definition.permission,
projectParameter: definition.projectParameter,
segments,
allowedQuery: compileAllowedQuery(definition.allowedQuery),
...(definition.validateQuery === undefined
? {}
: { validateQuery: definition.validateQuery }),
handle: definition.handle,
});
}
function routesOverlap(left: CompiledRoute, right: CompiledRoute): boolean {
if (
left.method !== right.method ||
left.segments.length !== right.segments.length
) {
return false;
}
return left.segments.every((segment, index) => {
const other = right.segments[index]!;
return (
segment.kind === 'parameter' ||
other.kind === 'parameter' ||
segment.value === other.value
);
});
}
function validateRequestPath(path: string): readonly string[] {
if (
typeof path !== 'string' ||
Buffer.byteLength(path) >
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxPathBytes ||
!(path === '/api/v3' || path.startsWith('/api/v3/')) ||
path.endsWith('/') ||
path.includes('//') ||
path.includes('%') ||
path.includes('\\') ||
CONTROL_CHARACTER_PATTERN.test(path)
) {
throw new ClusterControlRouteResolutionError(
400,
'invalid_route_path',
'Cluster-control route path is invalid',
);
}
const segments = path.slice(1).split('/');
if (
segments.length > CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxPathSegments ||
segments.some((segment) => !PARAMETER_VALUE_PATTERN.test(segment))
) {
throw new ClusterControlRouteResolutionError(
400,
'invalid_route_path',
'Cluster-control route path is invalid',
);
}
return segments;
}
function validateQuery(
query: Readonly<Record<string, readonly string[]>>,
allowed: ReadonlySet<string>,
): void {
if (!query || typeof query !== 'object' || Array.isArray(query)) {
throw new ClusterControlRouteResolutionError(
400,
'invalid_route_query',
'Cluster-control route query is invalid',
);
}
const names = Object.keys(query);
for (const name of names) {
const values = query[name];
if (
!allowed.has(name) ||
!Array.isArray(values) ||
values.length === 0 ||
values.length >
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxQueryValuesPerParameter ||
values.some(
(value) =>
typeof value !== 'string' ||
Buffer.byteLength(value) >
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxQueryValueBytes ||
CONTROL_CHARACTER_PATTERN.test(value),
)
) {
throw new ClusterControlRouteResolutionError(
400,
'invalid_route_query',
'Cluster-control route query is invalid',
);
}
}
}
function matchRoute(
route: CompiledRoute,
method: ClusterControlHttpMethod,
segments: readonly string[],
): ClusterControlRouteParameters | null {
if (route.method !== method || route.segments.length !== segments.length) {
return null;
}
const parameters = Object.create(null) as Record<string, string>;
for (let index = 0; index < segments.length; index += 1) {
const definition = route.segments[index]!;
const value = segments[index]!;
if (definition.kind === 'literal') {
if (definition.value !== value) return null;
} else {
parameters[definition.name] = value;
}
}
return Object.freeze(parameters);
}
/** Returns true only for an object created by the reviewed registry factory. */
export function isClusterControlRouteRegistry(
value: unknown,
): value is ClusterControlRouteRegistry {
return (
!!value &&
typeof value === 'object' &&
reviewedRegistries.has(value as object)
);
}
/**
* Compiles a bounded, immutable and non-overlapping route table. Route-owned
* operation, permission and Project scope are resolved before authentication.
*/
export function createClusterControlRouteRegistry(
definitions: readonly ClusterControlRouteDefinition[],
): ClusterControlRouteRegistry {
if (
!Array.isArray(definitions) ||
definitions.length > CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxRoutes
) {
throw configurationError('definitions must be a bounded array');
}
const routes = definitions.map(compileRoute);
const operationIds = new Set<string>();
for (let index = 0; index < routes.length; index += 1) {
const route = routes[index]!;
if (operationIds.has(route.operationId)) {
throw configurationError('operationId must be unique');
}
operationIds.add(route.operationId);
for (let otherIndex = 0; otherIndex < index; otherIndex += 1) {
if (routesOverlap(route, routes[otherIndex]!)) {
throw configurationError('route definitions overlap');
}
}
}
const registry: ClusterControlRouteRegistry = {
contractVersion: 1,
size: routes.length,
resolve(request) {
const segments = validateRequestPath(request.path);
for (const route of routes) {
const parameters = matchRoute(route, request.method, segments);
if (!parameters) continue;
validateQuery(request.query, route.allowedQuery);
if (route.validateQuery) {
try {
route.validateQuery(request.query);
} catch {
throw new ClusterControlRouteResolutionError(
400,
'invalid_route_query',
'Cluster-control route query is invalid',
);
}
}
const projectId =
route.projectParameter === null
? null
: parameters[route.projectParameter]!;
return Object.freeze({
operationId: route.operationId,
permission: route.permission,
projectId,
handle(request: ClusterControlAuthorizedOperationRequest) {
return route.handle(request, parameters);
},
});
}
return null;
},
};
reviewedRegistries.add(registry);
return Object.freeze(registry);
}
@@ -0,0 +1,99 @@
// Cluster Control Worker Ingress boundary; keep production PostgreSQL composition explicit.
import {
assertPostgresWorkerIngressSchemaReady,
PostgresSecurityAuditRepository,
PostgresWorkerCredentialRepository,
PostgresWorkerExecutionAttestationRepository,
PostgresWorkerSessionRepository,
} from '@qinglong/cluster-postgres/worker-ingress';
import {
startClusterWorkerIngressApplication,
type ClusterWorkerIngressApplicationResult,
} from './workerIngressApplication';
import {
createClusterWorkerIngressDatabaseOpener,
createClusterWorkerIngressHttpOptions,
type EnabledClusterWorkerIngressConfig,
} from './workerIngressConfig';
import {
createWorkerCredentialAuthenticator,
} from './workerCredentialAuthenticator';
import {
createWorkerIngressAdmissionPipeline,
} from './workerIngressPipeline';
import type { ClusterWorkerRuntimePort } from '../remote-execution/workerRuntimePort';
export interface ProductionClusterWorkerIngressOptions {
readonly config: EnabledClusterWorkerIngressConfig;
readonly runtime: ClusterWorkerRuntimePort;
readonly onPoolError?: (error: Error) => void;
}
/**
* Starts the reviewed Worker-facing listener. The worker-ingress Pool is used
* only for authentication, Session, attestation and audit authority. Every
* Run/Attempt/Lease mutation crosses the injected runtime capability port.
*/
export async function startProductionClusterWorkerIngress(
options: ProductionClusterWorkerIngressOptions,
): Promise<ClusterWorkerIngressApplicationResult> {
if (
!options ||
typeof options !== 'object' ||
!options.config?.enabled ||
!options.runtime
) {
throw new TypeError('Production Worker ingress options are invalid');
}
if (
options.onPoolError !== undefined &&
typeof options.onPoolError !== 'function'
) {
throw new TypeError('Production Worker ingress Pool error sink is invalid');
}
const http = await createClusterWorkerIngressHttpOptions(options.config);
const openDatabase = createClusterWorkerIngressDatabaseOpener(
options.config,
(error) => options.onPoolError?.(error),
);
return startClusterWorkerIngressApplication({
enabled: true,
profile: 'cluster-control',
workerCredentialPepper:
options.config.security.workerCredentialPepper,
openDatabase,
http,
async create({ database, workerCredentialPepper }) {
const report = await assertPostgresWorkerIngressSchemaReady(
database.pool,
);
return Object.freeze({
evidence: Object.freeze({
contractName: report.contractName,
contractVersion: report.contractVersion,
serverMajor: report.serverMajor,
migrationIds: Object.freeze([...report.migrationIds]),
}),
pipeline: createWorkerIngressAdmissionPipeline({
authenticator: createWorkerCredentialAuthenticator(
new PostgresWorkerCredentialRepository(database.pool),
workerCredentialPepper,
),
workers: new PostgresWorkerSessionRepository(database.pool),
attestations: new PostgresWorkerExecutionAttestationRepository(
database.pool,
),
audit: new PostgresSecurityAuditRepository(database.pool),
offers: options.runtime.offers,
activation: options.runtime.activation,
...(options.runtime.secrets === undefined
? {}
: { secrets: options.runtime.secrets }),
artifacts: options.runtime.artifacts,
completion: options.runtime.completion,
leaseControl: options.runtime.leaseControl,
}),
});
},
});
}
@@ -0,0 +1,96 @@
// Cluster Control Worker Ingress boundary; keep Worker credential authentication explicit.
import { timingSafeEqual } from 'node:crypto';
import {
WorkerCredentialUnavailableError,
normalizeWorkerCredentialRecord,
type WorkerCredentialRepository,
} from '@qinglong/runtime-core/worker-credential';
import {
assertWorkerCredentialPepper,
workerCredentialSecretDigest,
} from '@qinglong/runtime-core/worker-credential-token';
import type { ClusterControlAdmissionMetadata } from '../transport/httpSurface';
export interface AuthenticatedWorkerPrincipal {
readonly workerId: string;
readonly credentialId: string;
readonly credentialVersion: number;
readonly authenticationId: string;
readonly authenticatedAtMs: number;
readonly expiresAtMs: number;
}
export interface WorkerCredentialAuthenticator {
authenticate(
metadata: ClusterControlAdmissionMetadata,
): Promise<Readonly<AuthenticatedWorkerPrincipal> | null>;
}
const AUTHORIZATION =
/^Worker ql3w_([A-Za-z0-9][A-Za-z0-9._:-]{0,63})_([A-Za-z0-9_-]{43})$/;
export function createWorkerCredentialAuthenticator(
repository: WorkerCredentialRepository,
pepper: string,
options: Readonly<{ now?: () => number; principalTtlMs?: number }> = {},
): WorkerCredentialAuthenticator {
if (!repository || typeof repository.resolve !== 'function') {
throw new TypeError('Worker credential authenticator repository is invalid');
}
assertWorkerCredentialPepper(pepper);
const now = options.now ?? Date.now;
const principalTtlMs = options.principalTtlMs ?? 60_000;
if (
!Number.isSafeInteger(principalTtlMs) ||
principalTtlMs < 1_000 ||
principalTtlMs > 300_000
) {
throw new RangeError('Worker credential principal TTL is invalid');
}
return Object.freeze({
async authenticate(metadata: ClusterControlAdmissionMetadata) {
const header = metadata.headers.authorization;
if (typeof header !== 'string') return null;
const match = AUTHORIZATION.exec(header);
if (!match) return null;
let presented: Buffer | undefined;
try {
presented = Buffer.from(
workerCredentialSecretDigest(pepper, match[1]!, match[2]!),
'hex',
);
const candidate = await repository.resolve(match[1]!);
if (metadata.signal.aborted) throw new WorkerCredentialUnavailableError();
const record = candidate ? normalizeWorkerCredentialRecord(candidate) : null;
const stored = record
? Buffer.from(record.secretDigest, 'hex')
: Buffer.alloc(32);
const matches = timingSafeEqual(presented, stored);
stored.fill(0);
if (!record || !matches) return null;
const nowMs = now();
if (
!Number.isSafeInteger(nowMs) ||
nowMs < 0 ||
record.state !== 'active' ||
record.notBeforeAtMs > nowMs ||
record.expiresAtMs <= nowMs
) return null;
return Object.freeze({
workerId: record.workerId,
credentialId: record.credentialId,
credentialVersion: record.version,
authenticationId: `worker_credential:${record.credentialId}:${record.version}`,
authenticatedAtMs: nowMs,
expiresAtMs: Math.min(record.expiresAtMs, nowMs + principalTtlMs),
});
} catch (error) {
if (error instanceof WorkerCredentialUnavailableError) throw error;
throw new WorkerCredentialUnavailableError();
} finally {
presented?.fill(0);
}
},
});
}
@@ -0,0 +1,170 @@
// Cluster Control Worker Ingress boundary; keep listener lifecycle authority explicit.
import type {
ClusterControlReadinessEvidence,
ClusterControlAdmissionDisposer,
DeploymentProfile,
OpenPostgresDatabase,
PostgresDatabaseResource,
} from '@qinglong/runtime-core';
import { assertWorkerCredentialPepper } from '@qinglong/runtime-core/worker-credential-token';
import { MAX_REMOTE_SECRET_DELIVERY_RESPONSE_BYTES } from '@qinglong/runtime-core/remote-secret-delivery';
import {
startClusterControlHttpSurface,
type ClusterControlAdmissionPipeline,
type ClusterControlHttpAddress,
type ClusterControlHttpSurfaceOptions,
type ClusterControlMutualTlsOptions,
} from '../transport/httpSurface';
export interface ClusterWorkerIngressAssemblyInput {
readonly database: PostgresDatabaseResource;
readonly workerCredentialPepper: string;
}
export interface ClusterWorkerIngressAssembly {
readonly evidence: ClusterControlReadinessEvidence;
readonly pipeline: ClusterControlAdmissionPipeline;
}
export interface ClusterWorkerIngressApplicationOptions {
readonly enabled?: boolean;
readonly profile: DeploymentProfile;
readonly workerCredentialPepper?: string;
readonly openDatabase: OpenPostgresDatabase;
readonly http: ClusterControlHttpSurfaceOptions;
readonly create: (
input: ClusterWorkerIngressAssemblyInput,
) => ClusterWorkerIngressAssembly | Promise<ClusterWorkerIngressAssembly>;
}
export type ClusterWorkerIngressApplicationResult =
| { readonly status: 'disabled'; stop(): Promise<'stopped'> }
| {
readonly status: 'active';
readonly protocol: 'https';
readonly transport: 'mutual-tls';
readonly address: ClusterControlHttpAddress;
readonly evidence: ClusterControlReadinessEvidence;
reloadTransport(options: ClusterControlMutualTlsOptions): number;
stop(): Promise<'stopped'>;
};
/**
* Separate Worker-facing composition root. It owns a dedicated listener and a
* worker-ingress database resource. Storage readiness and repositories are
* supplied by the outer composition root, so this transport layer cannot
* acquire Project Policy, dispatch, recovery-claim or DDL authority itself.
*/
export async function startClusterWorkerIngressApplication(
options: ClusterWorkerIngressApplicationOptions,
): Promise<ClusterWorkerIngressApplicationResult> {
if (!(options.enabled ?? false)) {
return Object.freeze({
status: 'disabled',
async stop() {
return 'stopped' as const;
},
});
}
if (options.profile !== 'cluster-control') {
throw new TypeError('Worker ingress requires cluster-control profile');
}
if (typeof options.create !== 'function') {
throw new TypeError('Worker ingress assembly factory is required');
}
if (!options.http?.mutualTls) {
throw new TypeError('Worker ingress requires mutual TLS');
}
assertWorkerCredentialPepper(options.workerCredentialPepper ?? '');
const bodyLimit = options.http.maxBodyBytes ?? 64 * 1024;
if (
!Number.isSafeInteger(bodyLimit) ||
bodyLimit < 1024 ||
bodyLimit > 64 * 1024
) {
throw new RangeError(
'Worker ingress body limit must be between 1 KiB and 64 KiB',
);
}
let database: PostgresDatabaseResource | undefined;
const http = await startClusterControlHttpSurface({
...options.http,
maxBodyBytes: bodyLimit,
maxResponseBytes: Math.min(
options.http.maxResponseBytes ?? MAX_REMOTE_SECRET_DELIVERY_RESPONSE_BYTES,
MAX_REMOTE_SECRET_DELIVERY_RESPONSE_BYTES,
),
maxInFlightRequests: Math.min(options.http.maxInFlightRequests ?? 64, 256),
});
let disposeAdmission: ClusterControlAdmissionDisposer | undefined;
try {
database = await options.openDatabase();
const assembly = await options.create({
database,
workerCredentialPepper: options.workerCredentialPepper!,
});
disposeAdmission = http.installAdmission(
assembly.evidence,
assembly.pipeline,
);
let stopPromise: Promise<'stopped'> | undefined;
return Object.freeze({
status: 'active' as const,
protocol: 'https' as const,
transport: 'mutual-tls' as const,
address: http.address,
evidence: assembly.evidence,
reloadTransport(mutualTls: ClusterControlMutualTlsOptions) {
return http.reloadMutualTls(mutualTls);
},
stop() {
stopPromise ??= (async () => {
let primary: unknown;
try {
await disposeAdmission?.();
} catch (error) {
primary = error;
}
try {
await database?.close();
} catch (error) {
primary ??= error;
}
try {
await http.close();
} catch (error) {
primary ??= error;
}
if (primary) throw primary;
return 'stopped' as const;
})();
return stopPromise;
},
});
} catch (error) {
try {
await disposeAdmission?.();
} catch {
/* preserve root */
}
try {
await database?.close();
} catch {
/* preserve root */
}
try {
await http.close();
} catch {
/* preserve root */
}
throw error;
}
}
export * from './workerCredentialAuthenticator';
export * from './workerIngressPipeline';
export * from '../remote-execution/remoteRunActivationService';
export * from '../remote-execution/remoteWorkerSecretDeliveryService';
export * from '../remote-execution/remoteWorkerCompletionService';
export * from '../remote-execution/remoteWorkerLeaseControlService';
@@ -0,0 +1,864 @@
// Cluster Control Worker Ingress boundary; keep fail-closed deployment configuration explicit.
import {
createPrivateKey,
createPublicKey,
timingSafeEqual,
X509Certificate,
type KeyObject,
} from 'node:crypto';
import { constants } from 'node:fs';
import { open } from 'node:fs/promises';
import { isAbsolute } from 'node:path';
import type {
DeploymentProfile,
OpenPostgresDatabase,
} from '@qinglong/runtime-core';
import {
createPostgresDatabaseOpener,
isPostgresTlsDnsServername,
loadPostgresConnectionEnvironment,
loadPostgresCertificateAuthorityFile,
type PostgresConnectionOptions,
type PostgresPoolOptions,
} from '@qinglong/cluster-postgres/runtime';
import type {
ClusterControlHttpSurfaceOptions,
ClusterControlMutualTlsOptions,
} from '../transport/httpSurface';
export type ClusterWorkerIngressEnvironment = Readonly<
Record<string, string | undefined>
>;
export interface DisabledClusterWorkerIngressConfig {
readonly enabled: false;
readonly profile: DeploymentProfile;
}
export interface ClusterWorkerArtifactS3Config {
readonly bucket: string;
readonly region: string;
readonly prefix?: string;
readonly expectedBucketOwner?: string;
readonly endpoint?: string;
readonly forcePathStyle: boolean;
readonly encryption:
| Readonly<{ readonly mode: 's3' }>
| Readonly<{ readonly mode: 'kms'; readonly keyId: string }>;
}
export interface ClusterWorkerMountedSecretConfig {
readonly provider: 'mounted-files';
readonly rootDirectory: string;
}
export interface EnabledClusterWorkerIngressConfig {
readonly enabled: true;
readonly profile: 'cluster-control';
readonly http: Omit<ClusterControlHttpSurfaceOptions, 'mutualTls'>;
readonly transport: Readonly<{
readonly privateKeyFile: string;
readonly certificateFile: string;
readonly clientCertificateAuthorityFile: string;
readonly clientCertificateRevocationListFile?: string;
}>;
readonly database: Readonly<{
readonly connection: PostgresConnectionOptions;
readonly pool: PostgresPoolOptions;
}>;
readonly security: Readonly<{
readonly workerCredentialPepper: string;
}>;
readonly artifact: Readonly<ClusterWorkerArtifactS3Config>;
readonly secret?: Readonly<ClusterWorkerMountedSecretConfig>;
}
export type ClusterWorkerIngressConfig =
| DisabledClusterWorkerIngressConfig
| EnabledClusterWorkerIngressConfig;
export class ClusterWorkerIngressConfigError extends TypeError {
constructor(message: string) {
super(`Worker ingress configuration is invalid: ${message}`);
this.name = 'ClusterWorkerIngressConfigError';
}
}
const PROFILES = new Set<DeploymentProfile>([
'edge',
'standalone',
'cluster-control',
'worker',
]);
const MAX_TLS_FILE_BYTES = 1024 * 1024;
function booleanValue(
environment: ClusterWorkerIngressEnvironment,
name: string,
defaultValue: boolean,
): boolean {
const value = environment[name];
if (value === undefined || value === '') return defaultValue;
if (value === 'true') return true;
if (value === 'false') return false;
throw new ClusterWorkerIngressConfigError(`${name} must be true or false`);
}
function integerValue(
environment: ClusterWorkerIngressEnvironment,
name: string,
defaultValue: number,
minimum: number,
maximum: number,
): number {
const value = environment[name];
if (value === undefined || value === '') return defaultValue;
if (!/^\d+$/.test(value)) {
throw new ClusterWorkerIngressConfigError(`${name} must be an integer`);
}
const parsed = Number(value);
if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) {
throw new ClusterWorkerIngressConfigError(
`${name} must be between ${minimum} and ${maximum}`,
);
}
return parsed;
}
function boundedValue(
environment: ClusterWorkerIngressEnvironment,
name: string,
maximumLength: number,
required = false,
): string | undefined {
const value = environment[name];
if (value === undefined || value === '') {
if (required) {
throw new ClusterWorkerIngressConfigError(`${name} is required`);
}
return undefined;
}
if (value.length > maximumLength || /[\0\r\n]/.test(value)) {
throw new ClusterWorkerIngressConfigError(`${name} is invalid`);
}
return value;
}
function deploymentProfile(
environment: ClusterWorkerIngressEnvironment,
): DeploymentProfile {
const value = environment.QL_DEPLOYMENT_PROFILE ?? 'standalone';
if (!PROFILES.has(value as DeploymentProfile)) {
throw new ClusterWorkerIngressConfigError(
'QL_DEPLOYMENT_PROFILE is invalid',
);
}
return value as DeploymentProfile;
}
function absoluteFile(
environment: ClusterWorkerIngressEnvironment,
name: string,
): string {
const value = boundedValue(environment, name, 4096, true)!;
if (!isAbsolute(value)) {
throw new ClusterWorkerIngressConfigError(`${name} must be absolute`);
}
return value;
}
function optionalAbsoluteFile(
environment: ClusterWorkerIngressEnvironment,
name: string,
): string | undefined {
const value = boundedValue(environment, name, 4096);
if (value === undefined) return undefined;
if (!isAbsolute(value)) {
throw new ClusterWorkerIngressConfigError(`${name} must be absolute`);
}
return value;
}
function workerCredentialPepper(
environment: ClusterWorkerIngressEnvironment,
): string {
const value = boundedValue(
environment,
'QL3_WORKER_CREDENTIAL_PEPPER',
64,
true,
)!;
if (!/^[A-Za-z0-9_-]{43}$/.test(value)) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_CREDENTIAL_PEPPER must be canonical base64url for 32 bytes',
);
}
const decoded = Buffer.from(value, 'base64url');
const canonical =
decoded.byteLength === 32 && decoded.toString('base64url') === value;
decoded.fill(0);
if (!canonical) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_CREDENTIAL_PEPPER must be canonical base64url for 32 bytes',
);
}
return value;
}
function databaseConnection(
environment: ClusterWorkerIngressEnvironment,
): PostgresConnectionOptions {
let connection: PostgresConnectionOptions;
try {
connection = loadPostgresConnectionEnvironment(environment, {
connectionString: 'QL3_POSTGRES_WORKER_INGRESS_URL',
host: 'QL3_POSTGRES_WORKER_INGRESS_HOST',
port: 'QL3_POSTGRES_WORKER_INGRESS_PORT',
database: 'QL3_POSTGRES_WORKER_INGRESS_DATABASE',
user: 'QL3_POSTGRES_WORKER_INGRESS_USER',
password: 'QL3_POSTGRES_WORKER_INGRESS_PASSWORD',
});
} catch (error) {
throw new ClusterWorkerIngressConfigError(
error instanceof Error
? error.message
: 'PostgreSQL Worker ingress connection is invalid',
);
}
const mode =
environment.QL3_WORKER_INGRESS_POSTGRES_TLS_MODE ?? 'verify-full';
if (mode !== 'verify-full' && mode !== 'disable') {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_INGRESS_POSTGRES_TLS_MODE must be verify-full or disable',
);
}
if (
mode === 'disable' &&
!booleanValue(
environment,
'QL3_WORKER_INGRESS_POSTGRES_ALLOW_INSECURE',
false,
)
) {
throw new ClusterWorkerIngressConfigError(
'disabling PostgreSQL TLS requires QL3_WORKER_INGRESS_POSTGRES_ALLOW_INSECURE=true',
);
}
const servername = boundedValue(
environment,
'QL3_WORKER_INGRESS_POSTGRES_TLS_SERVERNAME',
253,
);
if (mode === 'verify-full' && !isPostgresTlsDnsServername(servername)) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_INGRESS_POSTGRES_TLS_SERVERNAME must be an explicit DNS name for verify-full',
);
}
const certificateAuthorityFile = boundedValue(
environment,
'QL3_WORKER_INGRESS_POSTGRES_TLS_CA_FILE',
4096,
);
if (mode === 'disable' && certificateAuthorityFile !== undefined) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_INGRESS_POSTGRES_TLS_CA_FILE cannot be used when TLS is disabled',
);
}
let certificateAuthority: string | undefined;
if (certificateAuthorityFile !== undefined) {
try {
certificateAuthority = loadPostgresCertificateAuthorityFile(
certificateAuthorityFile,
);
} catch {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_INGRESS_POSTGRES_TLS_CA_FILE must contain a bounded trusted CA bundle',
);
}
}
return Object.freeze({
...connection,
tls:
mode === 'disable'
? Object.freeze({ mode: 'disable' as const })
: Object.freeze({
mode: 'verify-full' as const,
...(certificateAuthority === undefined
? {}
: { ca: certificateAuthority }),
servername: servername!,
}),
});
}
function workerArtifactS3(
environment: ClusterWorkerIngressEnvironment,
): Readonly<ClusterWorkerArtifactS3Config> {
const bucket = boundedValue(
environment,
'QL3_WORKER_ARTIFACT_S3_BUCKET',
63,
true,
)!;
if (
!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(bucket) ||
bucket.includes('..') ||
/^\d{1,3}(?:\.\d{1,3}){3}$/.test(bucket)
) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_BUCKET is invalid',
);
}
const region = boundedValue(
environment,
'QL3_WORKER_ARTIFACT_S3_REGION',
63,
true,
)!;
if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(region)) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_REGION is invalid',
);
}
const prefix = boundedValue(
environment,
'QL3_WORKER_ARTIFACT_S3_PREFIX',
255,
);
if (
prefix !== undefined &&
(
!/^[A-Za-z0-9][A-Za-z0-9/_=-]{0,254}$/.test(prefix) ||
prefix.startsWith('/') ||
prefix.endsWith('/') ||
prefix.includes('//') ||
prefix.split('/').some((segment) => segment === '.' || segment === '..')
)
) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_PREFIX is invalid',
);
}
const expectedBucketOwner = boundedValue(
environment,
'QL3_WORKER_ARTIFACT_S3_EXPECTED_BUCKET_OWNER',
12,
);
if (
expectedBucketOwner !== undefined &&
!/^\d{12}$/.test(expectedBucketOwner)
) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_EXPECTED_BUCKET_OWNER must be 12 digits',
);
}
const endpointValue = boundedValue(
environment,
'QL3_WORKER_ARTIFACT_S3_ENDPOINT',
2048,
);
let endpoint: string | undefined;
if (endpointValue !== undefined) {
let parsed: URL;
try {
parsed = new URL(endpointValue);
} catch {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_ENDPOINT is invalid',
);
}
const allowInsecure = booleanValue(
environment,
'QL3_WORKER_ARTIFACT_S3_ALLOW_INSECURE',
false,
);
if (
(parsed.protocol !== 'https:' &&
!(parsed.protocol === 'http:' && allowInsecure)) ||
parsed.username !== '' ||
parsed.password !== '' ||
parsed.search !== '' ||
parsed.hash !== '' ||
parsed.pathname !== '/'
) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_ENDPOINT must be an origin URL; HTTP requires explicit insecure opt-in',
);
}
endpoint = parsed.origin;
}
const encryptionMode =
boundedValue(
environment,
'QL3_WORKER_ARTIFACT_S3_ENCRYPTION',
3,
) ?? 's3';
if (encryptionMode !== 's3' && encryptionMode !== 'kms') {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_ENCRYPTION must be s3 or kms',
);
}
const keyId = boundedValue(
environment,
'QL3_WORKER_ARTIFACT_S3_KMS_KEY_ID',
2048,
);
if (
(encryptionMode === 'kms' && keyId === undefined) ||
(encryptionMode === 's3' && keyId !== undefined)
) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_ARTIFACT_S3_KMS_KEY_ID must be present exactly for kms encryption',
);
}
return Object.freeze({
bucket,
region,
...(prefix === undefined ? {} : { prefix }),
...(expectedBucketOwner === undefined
? {}
: { expectedBucketOwner }),
...(endpoint === undefined ? {} : { endpoint }),
forcePathStyle: booleanValue(
environment,
'QL3_WORKER_ARTIFACT_S3_FORCE_PATH_STYLE',
false,
),
encryption:
encryptionMode === 's3'
? Object.freeze({ mode: 's3' as const })
: Object.freeze({ mode: 'kms' as const, keyId: keyId! }),
});
}
function workerSecret(
environment: ClusterWorkerIngressEnvironment,
): Readonly<ClusterWorkerMountedSecretConfig> | undefined {
const provider = boundedValue(
environment,
'QL3_WORKER_SECRET_PROVIDER',
32,
);
if (provider === undefined || provider === 'disabled') return undefined;
if (provider !== 'mounted-files') {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_SECRET_PROVIDER must be disabled or mounted-files',
);
}
return Object.freeze({
provider,
rootDirectory: absoluteFile(
environment,
'QL3_WORKER_SECRET_ROOT_DIRECTORY',
),
});
}
/**
* Applies the Profile gate before reading database, Worker secret or TLS file
* configuration. Disabled edge/standalone installs therefore remain free of
* Worker ingress credential and filesystem requirements.
*/
export function loadClusterWorkerIngressConfig(
environment: ClusterWorkerIngressEnvironment,
): ClusterWorkerIngressConfig {
if (
!environment ||
typeof environment !== 'object' ||
Array.isArray(environment)
) {
throw new ClusterWorkerIngressConfigError('environment must be an object');
}
const profile = deploymentProfile(environment);
const enabled = booleanValue(
environment,
'QL3_WORKER_INGRESS_ENABLED',
false,
);
if (!enabled) return Object.freeze({ enabled: false, profile });
if (profile !== 'cluster-control') {
throw new ClusterWorkerIngressConfigError(
'enabled ingress requires QL_DEPLOYMENT_PROFILE=cluster-control',
);
}
const applicationName =
boundedValue(
environment,
'QL3_WORKER_INGRESS_POSTGRES_APPLICATION_NAME',
63,
) ?? 'qinglong-worker-ingress';
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,62}$/.test(applicationName)) {
throw new ClusterWorkerIngressConfigError(
'QL3_WORKER_INGRESS_POSTGRES_APPLICATION_NAME is invalid',
);
}
const host =
boundedValue(environment, 'QL3_WORKER_INGRESS_HOST', 253) ?? '0.0.0.0';
const secret = workerSecret(environment);
return Object.freeze({
enabled: true,
profile: 'cluster-control',
http: Object.freeze({
host,
port: integerValue(
environment,
'QL3_WORKER_INGRESS_PORT',
5801,
1,
65_535,
),
maxBodyBytes: integerValue(
environment,
'QL3_WORKER_INGRESS_MAX_BODY_BYTES',
64 * 1024,
1024,
64 * 1024,
),
maxResponseBytes: integerValue(
environment,
'QL3_WORKER_INGRESS_MAX_RESPONSE_BYTES',
64 * 1024,
1024,
64 * 1024,
),
maxInFlightRequests: integerValue(
environment,
'QL3_WORKER_INGRESS_MAX_IN_FLIGHT',
64,
1,
256,
),
authenticationRateWindowMs: integerValue(
environment,
'QL3_WORKER_INGRESS_AUTH_RATE_WINDOW_MS',
60_000,
1_000,
60 * 60_000,
),
authenticationRatePerPeer: integerValue(
environment,
'QL3_WORKER_INGRESS_AUTH_RATE_PER_PEER',
120,
1,
1_000_000,
),
authenticationRateGlobal: integerValue(
environment,
'QL3_WORKER_INGRESS_AUTH_RATE_GLOBAL',
1_200,
1,
1_000_000,
),
authenticationRateMaxPeers: integerValue(
environment,
'QL3_WORKER_INGRESS_AUTH_RATE_MAX_PEERS',
4_096,
1,
65_536,
),
requestTimeoutMs: integerValue(
environment,
'QL3_WORKER_INGRESS_REQUEST_TIMEOUT_MS',
15_000,
100,
120_000,
),
drainTimeoutMs: integerValue(
environment,
'QL3_WORKER_INGRESS_DRAIN_TIMEOUT_MS',
10_000,
100,
120_000,
),
}),
transport: Object.freeze({
privateKeyFile: absoluteFile(
environment,
'QL3_WORKER_INGRESS_TLS_PRIVATE_KEY_FILE',
),
certificateFile: absoluteFile(
environment,
'QL3_WORKER_INGRESS_TLS_CERTIFICATE_FILE',
),
clientCertificateAuthorityFile: absoluteFile(
environment,
'QL3_WORKER_INGRESS_TLS_CLIENT_CA_FILE',
),
...(() => {
const clientCertificateRevocationListFile = optionalAbsoluteFile(
environment,
'QL3_WORKER_INGRESS_TLS_CLIENT_CRL_FILE',
);
return clientCertificateRevocationListFile === undefined
? {}
: { clientCertificateRevocationListFile };
})(),
}),
database: Object.freeze({
connection: databaseConnection(environment),
pool: Object.freeze({
applicationName,
maxConnections: integerValue(
environment,
'QL3_WORKER_INGRESS_POSTGRES_MAX_CONNECTIONS',
4,
1,
16,
),
connectionTimeoutMs: integerValue(
environment,
'QL3_WORKER_INGRESS_POSTGRES_CONNECTION_TIMEOUT_MS',
5_000,
100,
60_000,
),
}),
}),
security: Object.freeze({
workerCredentialPepper: workerCredentialPepper(environment),
}),
artifact: workerArtifactS3(environment),
...(secret === undefined ? {} : { secret }),
});
}
async function readTlsFile(
path: string,
privateMaterial: boolean,
): Promise<Buffer> {
let handle;
try {
handle = await open(path, constants.O_RDONLY);
const stat = await handle.stat();
if (
!stat.isFile() ||
stat.size < 1 ||
stat.size > MAX_TLS_FILE_BYTES ||
(privateMaterial && (stat.mode & 0o022) !== 0)
) {
throw new ClusterWorkerIngressConfigError('TLS file metadata is unsafe');
}
const bytes = await handle.readFile();
if (bytes.byteLength < 1 || bytes.byteLength > MAX_TLS_FILE_BYTES) {
bytes.fill(0);
throw new ClusterWorkerIngressConfigError('TLS file size is unsafe');
}
return bytes;
} catch (error) {
if (error instanceof ClusterWorkerIngressConfigError) throw error;
throw new ClusterWorkerIngressConfigError('TLS material is unavailable');
} finally {
await handle?.close().catch(() => undefined);
}
}
function activeCertificate(
name: string,
bytes: Buffer,
now: number,
): X509Certificate {
let certificate: X509Certificate;
try {
certificate = new X509Certificate(bytes);
} catch {
throw new ClusterWorkerIngressConfigError(
`${name} is not an X.509 certificate`,
);
}
const validFrom = Date.parse(certificate.validFrom);
const validTo = Date.parse(certificate.validTo);
if (
!Number.isFinite(validFrom) ||
!Number.isFinite(validTo) ||
now < validFrom ||
now >= validTo
) {
throw new ClusterWorkerIngressConfigError(`${name} is not currently valid`);
}
return certificate;
}
function activeCertificateAuthorities(
bytes: Buffer,
now: number,
): readonly Buffer[] {
const pem = bytes.toString('utf8');
const matches = pem.match(
/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g,
);
if (!matches || matches.length < 1 || matches.length > 16) {
throw new ClusterWorkerIngressConfigError(
'TLS client certificate authority bundle must contain 1 to 16 PEM certificates',
);
}
const remainder = matches.reduce(
(value, certificate) => value.replace(certificate, ''),
pem,
);
if (remainder.trim() !== '') {
throw new ClusterWorkerIngressConfigError(
'TLS client certificate authority bundle contains unsupported data',
);
}
const authorities: Buffer[] = [];
try {
for (const match of matches) {
const authorityBytes = Buffer.from(`${match}\n`, 'utf8');
const authority = activeCertificate(
'TLS client certificate authority',
authorityBytes,
now,
);
if (!authority.ca) {
authorityBytes.fill(0);
throw new ClusterWorkerIngressConfigError(
'TLS client certificate authority is not a CA',
);
}
authorities.push(authorityBytes);
}
return Object.freeze(authorities);
} catch (error) {
for (const authority of authorities) authority.fill(0);
throw error;
}
}
function certificateRevocationList(bytes: Buffer): Buffer {
const value = bytes.toString('utf8').trim();
if (
!value.startsWith('-----BEGIN X509 CRL-----') ||
!value.endsWith('-----END X509 CRL-----')
) {
throw new ClusterWorkerIngressConfigError(
'TLS client certificate revocation list is not a PEM CRL',
);
}
return bytes;
}
function matchingPrivateKey(
privateKey: KeyObject,
certificate: X509Certificate,
): boolean {
const key = createPublicKey(privateKey).export({
type: 'spki',
format: 'der',
});
const certificateKey = certificate.publicKey.export({
type: 'spki',
format: 'der',
});
return (
key.byteLength === certificateKey.byteLength &&
timingSafeEqual(key, certificateKey)
);
}
export async function loadClusterWorkerIngressMutualTls(
config: EnabledClusterWorkerIngressConfig,
now: number = Date.now(),
): Promise<ClusterControlMutualTlsOptions> {
if (!config?.enabled || config.profile !== 'cluster-control') {
throw new ClusterWorkerIngressConfigError(
'TLS material requires an enabled Worker ingress config',
);
}
if (!Number.isSafeInteger(now) || now < 0) {
throw new ClusterWorkerIngressConfigError('observation time is invalid');
}
const keyBytes = await readTlsFile(config.transport.privateKeyFile, true);
let certificateBytes: Buffer | undefined;
let clientAuthorityBundleBytes: Buffer | undefined;
let certificateRevocationListBytes: Buffer | undefined;
let clientCertificateAuthorities: readonly Buffer[] = Object.freeze([]);
try {
let privateKey: KeyObject;
try {
privateKey = createPrivateKey(keyBytes);
} catch {
throw new ClusterWorkerIngressConfigError('TLS private key is invalid');
}
certificateBytes = await readTlsFile(
config.transport.certificateFile,
false,
);
clientAuthorityBundleBytes = await readTlsFile(
config.transport.clientCertificateAuthorityFile,
false,
);
const certificate = activeCertificate(
'TLS server certificate',
certificateBytes,
now,
);
clientCertificateAuthorities = activeCertificateAuthorities(
clientAuthorityBundleBytes,
now,
);
if (!matchingPrivateKey(privateKey, certificate)) {
throw new ClusterWorkerIngressConfigError(
'TLS private key does not match the server certificate',
);
}
if (config.transport.clientCertificateRevocationListFile !== undefined) {
certificateRevocationListBytes = certificateRevocationList(
await readTlsFile(
config.transport.clientCertificateRevocationListFile,
false,
),
);
}
const mutualTls: ClusterControlMutualTlsOptions = Object.freeze({
privateKey: keyBytes,
certificateChain: certificateBytes,
clientCertificateAuthorities,
...(certificateRevocationListBytes === undefined
? {}
: {
certificateRevocationLists: Object.freeze([
certificateRevocationListBytes,
]),
}),
});
clientAuthorityBundleBytes.fill(0);
return mutualTls;
} catch (error) {
keyBytes.fill(0);
certificateBytes?.fill(0);
clientAuthorityBundleBytes?.fill(0);
certificateRevocationListBytes?.fill(0);
for (const authority of clientCertificateAuthorities) authority.fill(0);
throw error;
}
}
export async function createClusterWorkerIngressHttpOptions(
config: EnabledClusterWorkerIngressConfig,
now: number = Date.now(),
): Promise<ClusterControlHttpSurfaceOptions> {
const mutualTls = await loadClusterWorkerIngressMutualTls(config, now);
return Object.freeze({ ...config.http, mutualTls });
}
export function createClusterWorkerIngressDatabaseOpener(
config: EnabledClusterWorkerIngressConfig,
onPoolError: (error: Error) => void,
): OpenPostgresDatabase {
if (!config?.enabled || config.profile !== 'cluster-control') {
throw new ClusterWorkerIngressConfigError(
'database opener requires an enabled Worker ingress config',
);
}
if (typeof onPoolError !== 'function') {
throw new ClusterWorkerIngressConfigError('onPoolError must be a function');
}
return createPostgresDatabaseOpener({
role: 'worker-ingress',
connection: config.database.connection,
pool: config.database.pool,
onPoolError,
});
}
@@ -0,0 +1,584 @@
// Cluster Control Worker Ingress boundary; keep authenticated admission routing explicit.
import { randomUUID } from 'node:crypto';
import {
WorkerSessionConflictError,
WorkerSessionFenceRejectedError,
} from '@qinglong/runtime-core';
import type {
AuthenticatedWorkerSessionRepository,
} from '@qinglong/runtime-core/worker-credential-delivery';
import {
WorkerCredentialDeliveryConflictError,
WorkerCredentialDeliveryUnavailableError,
} from '@qinglong/runtime-core/worker-credential-delivery';
import {
InvalidWorkerSessionTransportError,
createWorkerSessionHeartbeatResponseBody,
createWorkerSessionRegisterResponseBody,
createWorkerSessionTransitionResponseBody,
parseWorkerSessionHeartbeatRequestBody,
parseWorkerSessionRegisterRequestBody,
parseWorkerSessionTransitionRequestBody,
} from '@qinglong/runtime-core/worker-session-transport';
import {
WorkerExecutionAttestationFenceRejectedError,
WorkerExecutionAttestationUnavailableError,
type WorkerExecutionAttestationRepository,
} from '@qinglong/runtime-core/worker-attestation';
import {
WorkerCredentialUnavailableError,
} from '@qinglong/runtime-core/worker-credential';
import {
normalizeSecurityAuditRecord,
type SecurityAuditSink,
} from '@qinglong/runtime-core/security-audit';
import type {
ClusterControlAdmissionMetadata,
ClusterControlAdmissionPipeline,
ClusterControlAdmissionResponse,
ClusterControlStreamingAdmissionBody,
} from '../transport/httpSurface';
import type {
AuthenticatedWorkerPrincipal,
WorkerCredentialAuthenticator,
} from './workerCredentialAuthenticator';
import {
ClusterRemoteWorkerOfferFenceRejectedError,
type ClusterRemoteWorkerOfferClaimService,
} from '../remote-execution/remoteWorkerDispatcher';
import {
RemoteRunActivationFenceRejectedError,
RemoteRunActivationUnavailableError,
} from '@qinglong/runtime-core/remote-activation';
import {
createRemoteRunActivationResponseBody,
InvalidRemoteRunActivationDeliveryError,
} from '@qinglong/runtime-core/remote-activation-delivery';
import type { ClusterRemoteRunActivationService } from '../remote-execution/remoteRunActivationService';
import {
createRemoteExecutionOfferPullBody,
InvalidRemoteExecutionOfferDeliveryError,
} from '@qinglong/runtime-core/remote-offer-delivery';
import {
createRemoteWorkerSecretDeliveryResponseBody,
InvalidRemoteWorkerSecretDeliveryError,
REMOTE_SECRET_DELIVERY_SCHEMA,
RemoteWorkerSecretDeliveryFenceRejectedError,
RemoteWorkerSecretDeliveryUnavailableError,
} from '@qinglong/runtime-core/remote-secret-delivery';
import type { ClusterRemoteWorkerSecretDeliveryService } from '../remote-execution/remoteWorkerSecretDeliveryService';
import {
InvalidRemoteWorkerCompletionError,
MAX_REMOTE_WORKER_ARTIFACT_BYTES,
MAX_REMOTE_WORKER_ARTIFACT_HEADER_BYTES,
REMOTE_WORKER_ARTIFACT_CONTENT_TYPE,
RemoteWorkerCompletionFenceRejectedError,
RemoteWorkerCompletionUnavailableError,
createRemoteWorkerArtifactUploadResponseBody,
createRemoteWorkerCompletionResponseBody,
parseRemoteWorkerCompletionRequestBody,
} from '@qinglong/runtime-core/remote-worker-completion';
import type {
ClusterRemoteWorkerArtifactService,
ClusterRemoteWorkerCompletionService,
} from '../remote-execution/remoteWorkerCompletionService';
import {
InvalidRemoteWorkerLeaseControlError,
RemoteWorkerLeaseControlFenceRejectedError,
RemoteWorkerLeaseControlUnavailableError,
createRemoteWorkerLeaseControlResponseBody,
parseRemoteWorkerLeaseControlRequestBody,
} from '@qinglong/runtime-core/remote-worker-lease-control';
import type { ClusterRemoteWorkerLeaseControlService } from '../remote-execution/remoteWorkerLeaseControlService';
export interface WorkerIngressPipelineOptions {
readonly authenticator: WorkerCredentialAuthenticator;
readonly workers: AuthenticatedWorkerSessionRepository;
readonly attestations: WorkerExecutionAttestationRepository;
readonly audit: SecurityAuditSink;
readonly offers?: Pick<ClusterRemoteWorkerOfferClaimService, 'claimNext'>;
readonly activation?: Pick<
ClusterRemoteRunActivationService,
'acknowledgeStarting' | 'acknowledgeRunning' | 'failStart'
>;
readonly secrets?: Pick<ClusterRemoteWorkerSecretDeliveryService, 'deliver'>;
readonly artifacts?: Pick<ClusterRemoteWorkerArtifactService, 'upload'>;
readonly completion?: Pick<ClusterRemoteWorkerCompletionService, 'complete'>;
readonly leaseControl?: Pick<ClusterRemoteWorkerLeaseControlService, 'control'>;
readonly now?: () => number;
}
type Operation =
| 'register'
| 'heartbeat'
| 'transition'
| 'attestations'
| 'offers'
| 'starting'
| 'running'
| 'start-failure'
| 'secrets'
| 'artifacts'
| 'completion'
| 'lease-control';
interface ResolvedRoute {
readonly workerId: string;
readonly sessionId: string;
readonly operation: Operation;
}
const ROUTE = /^\/api\/v3\/worker-ingress\/workers\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/sessions\/([0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\/(register|heartbeat|transition|attestations|offers|starting|running|start-failure|secrets|artifacts|completion|lease-control)$/;
function failure(statusCode: number, code: string): Error {
return Object.assign(new Error(code), { statusCode, code });
}
function route(metadata: ClusterControlAdmissionMetadata): ResolvedRoute {
if (metadata.method !== 'POST' || Object.keys(metadata.query).length !== 0) {
throw failure(404, 'worker_route_not_found');
}
const match = ROUTE.exec(metadata.path);
if (!match) throw failure(404, 'worker_route_not_found');
return Object.freeze({
workerId: match[1]!,
sessionId: match[2]!,
operation: match[3]! as Operation,
});
}
function objectBody(body: unknown | null, keys: readonly string[]): Record<string, unknown> {
if (!body || typeof body !== 'object' || Array.isArray(body)) {
throw failure(400, 'invalid_worker_request');
}
const actual = Object.keys(body).sort();
const expected = [...keys].sort();
if (
actual.length !== expected.length ||
actual.some((key, index) => key !== expected[index])
) throw failure(400, 'invalid_worker_request');
return body as Record<string, unknown>;
}
async function audit(
sink: SecurityAuditSink,
metadata: ClusterControlAdmissionMetadata,
operation: Operation,
principal: Readonly<AuthenticatedWorkerPrincipal> | null,
outcome: 'authentication_rejected' | 'authentication_unavailable' | 'allowed',
now: () => number,
): Promise<void> {
await sink.record(normalizeSecurityAuditRecord({
eventId: randomUUID(),
requestId: metadata.requestId,
operationId: `worker.${operation}`,
projectId: null,
subject: principal ? { type: 'worker', id: principal.workerId } : null,
authenticationId: principal?.authenticationId ?? null,
outcome,
reasons: [outcome === 'allowed' ? 'worker_credential' : outcome],
fence: null,
occurredAtMs: now(),
}));
}
function response(statusCode: number, body: unknown): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body });
}
function mapIngressFailure(error: unknown): never {
if (error && typeof error === 'object' && 'statusCode' in error) throw error;
if (
error instanceof WorkerSessionConflictError ||
error instanceof WorkerSessionFenceRejectedError ||
error instanceof WorkerCredentialDeliveryConflictError
) throw failure(409, 'worker_session_fenced');
if (error instanceof WorkerExecutionAttestationFenceRejectedError) {
throw failure(409, 'worker_attestation_fenced');
}
if (error instanceof ClusterRemoteWorkerOfferFenceRejectedError) {
throw failure(409, 'worker_offer_fenced');
}
if (error instanceof RemoteRunActivationFenceRejectedError) {
throw failure(409, 'worker_activation_fenced');
}
if (error instanceof RemoteWorkerSecretDeliveryFenceRejectedError) {
throw failure(409, 'worker_secret_delivery_fenced');
}
if (error instanceof RemoteWorkerCompletionFenceRejectedError) {
throw failure(409, 'worker_completion_fenced');
}
if (error instanceof RemoteWorkerLeaseControlFenceRejectedError) {
throw failure(409, 'worker_lease_control_fenced');
}
if (error instanceof InvalidRemoteWorkerCompletionError) {
throw failure(400, 'invalid_worker_request');
}
if (error instanceof InvalidRemoteWorkerLeaseControlError) {
throw failure(400, 'invalid_worker_request');
}
if (error instanceof InvalidWorkerSessionTransportError) {
throw failure(400, 'invalid_worker_request');
}
if (
error instanceof InvalidRemoteExecutionOfferDeliveryError ||
error instanceof InvalidRemoteRunActivationDeliveryError ||
error instanceof InvalidRemoteWorkerSecretDeliveryError
) throw failure(503, 'worker_ingress_unavailable');
if (
error instanceof WorkerExecutionAttestationUnavailableError ||
error instanceof WorkerCredentialUnavailableError ||
error instanceof WorkerCredentialDeliveryUnavailableError ||
error instanceof RemoteRunActivationUnavailableError ||
error instanceof RemoteWorkerSecretDeliveryUnavailableError ||
error instanceof RemoteWorkerCompletionUnavailableError ||
error instanceof RemoteWorkerLeaseControlUnavailableError
) throw failure(503, 'worker_ingress_unavailable');
if (error instanceof TypeError || error instanceof RangeError) {
throw failure(400, 'invalid_worker_request');
}
throw failure(503, 'worker_ingress_unavailable');
}
export function createWorkerIngressAdmissionPipeline(
options: WorkerIngressPipelineOptions,
): ClusterControlAdmissionPipeline {
if (
!options ||
typeof options.authenticator?.authenticate !== 'function' ||
typeof options.workers?.register !== 'function' ||
typeof options.workers?.heartbeatAuthenticated !== 'function' ||
typeof options.workers?.transitionAuthenticated !== 'function' ||
typeof options.attestations?.submit !== 'function' ||
typeof options.audit?.record !== 'function' ||
(options.offers !== undefined &&
typeof options.offers.claimNext !== 'function') ||
(options.activation !== undefined &&
(typeof options.activation.acknowledgeStarting !== 'function' ||
typeof options.activation.acknowledgeRunning !== 'function' ||
typeof options.activation.failStart !== 'function')) ||
(options.secrets !== undefined &&
typeof options.secrets.deliver !== 'function') ||
(options.artifacts !== undefined &&
typeof options.artifacts.upload !== 'function') ||
(options.completion !== undefined &&
typeof options.completion.complete !== 'function') ||
(options.leaseControl !== undefined &&
typeof options.leaseControl.control !== 'function')
) throw new TypeError('Worker ingress pipeline options are invalid');
const now = options.now ?? Date.now;
return Object.freeze({
async prepare(metadata: ClusterControlAdmissionMetadata) {
const resolved = route(metadata);
let principal: Readonly<AuthenticatedWorkerPrincipal> | null;
try {
principal = await options.authenticator.authenticate(metadata);
} catch {
try { await audit(options.audit, metadata, resolved.operation, null, 'authentication_unavailable', now); } catch { /* fail below */ }
throw failure(503, 'worker_authentication_unavailable');
}
if (!principal || principal.workerId !== resolved.workerId) {
try { await audit(options.audit, metadata, resolved.operation, null, 'authentication_rejected', now); } catch { throw failure(503, 'worker_audit_unavailable'); }
throw failure(401, 'worker_authentication_required');
}
try {
await audit(options.audit, metadata, resolved.operation, principal, 'allowed', now);
} catch {
throw failure(503, 'worker_audit_unavailable');
}
if (resolved.operation === 'artifacts') {
return Object.freeze({
bodyMode: 'stream' as const,
contentType: REMOTE_WORKER_ARTIFACT_CONTENT_TYPE,
maximumBodyBytes:
4 + MAX_REMOTE_WORKER_ARTIFACT_HEADER_BYTES +
MAX_REMOTE_WORKER_ARTIFACT_BYTES,
async handleStream(body: ClusterControlStreamingAdmissionBody) {
try {
if (!options.artifacts) {
throw failure(503, 'worker_artifact_unavailable');
}
const receipt = await options.artifacts.upload({
workerId: resolved.workerId,
workerSessionId: resolved.sessionId,
contentLength: body.contentLength,
chunks: body.chunks,
signal: metadata.signal,
});
return response(
200,
createRemoteWorkerArtifactUploadResponseBody(receipt),
);
} catch (error) {
return mapIngressFailure(error);
}
},
});
}
return Object.freeze({
async handle(body: unknown | null) {
try {
if (resolved.operation === 'register') {
const command = parseWorkerSessionRegisterRequestBody(body, {
workerId: resolved.workerId,
sessionId: resolved.sessionId,
});
const result = await options.workers.register(command);
return response(
200,
createWorkerSessionRegisterResponseBody(result),
);
}
if (resolved.operation === 'heartbeat') {
const command = parseWorkerSessionHeartbeatRequestBody(body, {
workerId: resolved.workerId,
sessionId: resolved.sessionId,
});
const worker = await options.workers.heartbeatAuthenticated(
command,
{
workerId: principal.workerId,
credentialId: principal.credentialId,
credentialVersion: principal.credentialVersion,
},
);
return response(
200,
createWorkerSessionHeartbeatResponseBody(worker),
);
}
if (resolved.operation === 'transition') {
const command = parseWorkerSessionTransitionRequestBody(body, {
workerId: resolved.workerId,
sessionId: resolved.sessionId,
});
const worker = await options.workers.transitionAuthenticated(
command,
{
workerId: principal.workerId,
credentialId: principal.credentialId,
credentialVersion: principal.credentialVersion,
},
);
return response(
200,
createWorkerSessionTransitionResponseBody(worker),
);
}
if (resolved.operation === 'offers') {
if (!options.offers) {
throw failure(503, 'worker_offer_unavailable');
}
const value = objectBody(body, [
'workerGeneration', 'offerId', 'leaseToken',
]);
const result = await options.offers.claimNext(
{ workerId: resolved.workerId },
{
workerSessionId: resolved.sessionId,
workerGeneration: value.workerGeneration as number,
offerId: value.offerId as string,
leaseToken: value.leaseToken as string,
},
);
return response(200, createRemoteExecutionOfferPullBody(result));
}
if (
resolved.operation === 'starting' ||
resolved.operation === 'start-failure'
) {
if (!options.activation) {
throw failure(503, 'worker_activation_unavailable');
}
const value = objectBody(body, [
'runId', 'attemptId', 'workerGeneration', 'offerId',
'leaseGeneration', 'leaseToken', 'expectedLeaseVersion',
]);
const command = {
runId: value.runId as string,
attemptId: value.attemptId as string,
workerSessionId: resolved.sessionId,
workerGeneration: value.workerGeneration as number,
offerId: value.offerId as string,
leaseGeneration: value.leaseGeneration as number,
leaseToken: value.leaseToken as string,
expectedLeaseVersion: value.expectedLeaseVersion as number,
};
const activation = resolved.operation === 'starting'
? await options.activation.acknowledgeStarting(
{ workerId: resolved.workerId }, command,
)
: await options.activation.failStart(
{ workerId: resolved.workerId }, command,
);
return response(
200,
createRemoteRunActivationResponseBody(activation),
);
}
if (resolved.operation === 'running') {
if (!options.activation) {
throw failure(503, 'worker_activation_unavailable');
}
const value = objectBody(body, [
'runId', 'attemptId', 'workerGeneration', 'offerId',
'leaseGeneration', 'leaseToken', 'expectedLeaseVersion',
'executorHandle', 'logArtifactId', 'callbackSequence',
'callbackTokenDigest',
]);
if (
value.logArtifactId !== null &&
typeof value.logArtifactId !== 'string'
) throw failure(400, 'invalid_worker_request');
const activation = await options.activation.acknowledgeRunning(
{ workerId: resolved.workerId },
{
runId: value.runId as string,
attemptId: value.attemptId as string,
workerSessionId: resolved.sessionId,
workerGeneration: value.workerGeneration as number,
offerId: value.offerId as string,
leaseGeneration: value.leaseGeneration as number,
leaseToken: value.leaseToken as string,
expectedLeaseVersion: value.expectedLeaseVersion as number,
executorHandle: value.executorHandle as string,
callbackSequence: value.callbackSequence as number,
callbackTokenDigest: value.callbackTokenDigest as string,
...(value.logArtifactId === null
? {}
: { logArtifactId: value.logArtifactId }),
},
);
return response(
200,
createRemoteRunActivationResponseBody(activation),
);
}
if (resolved.operation === 'secrets') {
if (!options.secrets) {
throw failure(503, 'worker_secret_delivery_unavailable');
}
const value = objectBody(body, [
'schema', 'runId', 'attemptId', 'projectId', 'taskId',
'taskRevision', 'executionDigest', 'workerGeneration',
'offerId', 'leaseGeneration', 'leaseToken',
'expectedLeaseVersion', 'secretRefs',
]);
if (value.schema !== REMOTE_SECRET_DELIVERY_SCHEMA) {
throw failure(400, 'invalid_worker_request');
}
const delivered = await options.secrets.deliver(
{ workerId: resolved.workerId },
{
workerSessionId: resolved.sessionId,
workerGeneration: value.workerGeneration as number,
runId: value.runId as string,
attemptId: value.attemptId as string,
projectId: value.projectId as string,
taskId: value.taskId as string,
taskRevision: value.taskRevision as string,
executionDigest: value.executionDigest as string,
offerId: value.offerId as string,
leaseGeneration: value.leaseGeneration as number,
leaseToken: value.leaseToken as string,
expectedLeaseVersion: value.expectedLeaseVersion as number,
secretRefs: value.secretRefs as string[],
},
);
try {
const responseBody = createRemoteWorkerSecretDeliveryResponseBody(
delivered,
value.secretRefs as string[],
);
if (
responseBody.runId !== value.runId ||
responseBody.attemptId !== value.attemptId ||
responseBody.offerId !== value.offerId ||
responseBody.executionDigest !== value.executionDigest
) throw new InvalidRemoteWorkerSecretDeliveryError(
'service response authority does not match request',
);
return response(
200,
responseBody,
);
} finally {
try { await delivered.dispose?.(); } catch { /* response remains valid */ }
}
}
if (resolved.operation === 'completion') {
if (!options.completion) {
throw failure(503, 'worker_completion_unavailable');
}
const command = parseRemoteWorkerCompletionRequestBody(body, {
workerId: resolved.workerId,
workerSessionId: resolved.sessionId,
});
const completed = await options.completion.complete(
command,
metadata.signal,
);
return response(
200,
createRemoteWorkerCompletionResponseBody(completed),
);
}
if (resolved.operation === 'lease-control') {
if (!options.leaseControl) {
throw failure(503, 'worker_lease_control_unavailable');
}
const command = parseRemoteWorkerLeaseControlRequestBody(body, {
workerId: resolved.workerId,
workerSessionId: resolved.sessionId,
});
return response(
200,
createRemoteWorkerLeaseControlResponseBody(
await options.leaseControl.control(command),
),
);
}
const value = objectBody(body, [
'attestationId', 'runId', 'attemptId', 'sequence', 'state',
'workerGeneration', 'leaseTokenDigest', 'leaseGeneration',
'leaseVersion', 'offerId', 'callbackSequence', 'executorHandle',
'journalRevision',
]);
if (value.workerGeneration === undefined) {
throw failure(400, 'invalid_worker_request');
}
const result = await options.attestations.submit({
attestationId: value.attestationId as string,
runId: value.runId as string,
attemptId: value.attemptId as string,
sequence: value.sequence as number,
state: value.state as 'running' | 'stopped',
workerId: resolved.workerId,
workerSessionId: resolved.sessionId,
workerGeneration: value.workerGeneration as number,
leaseTokenDigest: value.leaseTokenDigest as string,
leaseGeneration: value.leaseGeneration as number,
leaseVersion: value.leaseVersion as number,
offerId: value.offerId as string,
callbackSequence: value.callbackSequence as number,
executorHandle: value.executorHandle as string,
journalRevision: value.journalRevision as number,
});
return response(result.status === 'created' ? 201 : 200, {
attestationId: result.attestation.attestationId,
sequence: result.attestation.sequence,
state: result.attestation.state,
receivedAtMs: result.attestation.receivedAtMs,
replay: result.status === 'existing',
});
} catch (error) {
return mapIngressFailure(error);
}
},
});
},
});
}
@@ -0,0 +1,282 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterControlAdmissionSecurityError,
createClusterControlAdmissionPipeline,
createClusterControlProjectPolicyAuthorizer,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
const NOW = 10_000;
const PRINCIPAL = Object.freeze({
subject: Object.freeze({ type: 'user', id: 'usr_primary' }),
authenticationId: 'session:abc123',
authenticatedAtMs: 9_000,
expiresAtMs: 11_000,
assurance: 'multi_factor',
});
const METADATA = Object.freeze({
requestId: 'request-1',
method: 'POST',
path: '/api/v3/projects/prj_default/runs',
query: Object.freeze({}),
headers: Object.freeze({ authorization: 'Bearer opaque' }),
signal: new AbortController().signal,
});
function options(overrides = {}) {
const events = overrides.events ?? [];
return {
routes: createClusterControlRouteRegistry([
{
method: 'POST',
path: '/api/v3/projects/{projectId}/runs',
operationId: 'run.create',
permission: 'run.start',
projectParameter: 'projectId',
handle(input, parameters) {
events.push(
`handle:${parameters.projectId}:${input.request.body.taskId}`,
);
return { statusCode: 202, body: { accepted: true } };
},
},
]),
authenticator: {
authenticate() {
events.push('authenticate');
return PRINCIPAL;
},
},
policy: {
authorize(request) {
events.push(`authorize:${request.permission}`);
return {
effect: 'allow',
reasons: ['role_grant'],
fence: { projectVersion: 2, bindingVersion: 3 },
};
},
},
audit: {
record(record) {
events.push(`audit:${record.outcome}`);
},
},
now: () => NOW,
...overrides,
};
}
test('authenticates, authorizes and audits before accepting a body', async () => {
const events = [];
const pipeline = createClusterControlAdmissionPipeline(options({ events }));
const prepared = await pipeline.prepare(METADATA);
assert.deepEqual(events, [
'authenticate',
'authorize:run.start',
'audit:allowed',
]);
assert.deepEqual(await prepared.handle({ taskId: 'task-1' }), {
statusCode: 202,
body: { accepted: true },
});
assert.deepEqual(events.slice(-1), ['handle:prj_default:task-1']);
});
test('requires a reviewed route registry and rejects unknown routes before authentication', async () => {
assert.throws(
() =>
createClusterControlAdmissionPipeline(
options({
routes: {
contractVersion: 1,
size: 1,
resolve() {
return null;
},
},
}),
),
/options are invalid/,
);
const events = [];
const pipeline = createClusterControlAdmissionPipeline(options({ events }));
await assert.rejects(
pipeline.prepare({
...METADATA,
path: '/api/v3/projects/prj_default/tasks',
}),
(error) =>
error instanceof ClusterControlAdmissionSecurityError &&
error.statusCode === 404 &&
error.code === 'route_not_found',
);
assert.deepEqual(events, []);
});
test('rejects missing authentication before policy and handler execution', async () => {
const events = [];
const pipeline = createClusterControlAdmissionPipeline(
options({
events,
authenticator: {
authenticate() {
events.push('authenticate');
return null;
},
},
}),
);
await assert.rejects(
pipeline.prepare(METADATA),
(error) =>
error instanceof ClusterControlAdmissionSecurityError &&
error.statusCode === 401 &&
error.code === 'authentication_required',
);
assert.equal(events.includes('authorize:run.start'), false);
assert.equal(
events.some((event) => event.startsWith('handle:')),
false,
);
assert.equal(events.includes('audit:authentication_rejected'), true);
});
test('maps policy decisions to low-sensitive deny and approval responses', async () => {
for (const [effect, code, outcome] of [
['deny', 'forbidden', 'denied'],
['require_approval', 'approval_required', 'approval_required'],
]) {
const events = [];
const pipeline = createClusterControlAdmissionPipeline(
options({
events,
policy: {
authorize() {
return { effect, reasons: ['policy_decision'], fence: null };
},
},
}),
);
await assert.rejects(
pipeline.prepare(METADATA),
(error) =>
error instanceof ClusterControlAdmissionSecurityError &&
error.statusCode === 403 &&
error.code === code &&
!error.message.includes('policy_decision'),
);
assert.equal(events.includes(`audit:${outcome}`), true);
}
});
test('fails closed when authentication, policy or security audit is unavailable', async () => {
const scenarios = [
{
override: {
authenticator: {
authenticate() {
throw new Error('identity database detail');
},
},
},
code: 'authentication_unavailable',
},
{
override: {
policy: {
authorize() {
throw new Error('policy database detail');
},
},
},
code: 'authorization_unavailable',
},
{
override: {
audit: {
record() {
throw new Error('audit store detail');
},
},
},
code: 'security_audit_unavailable',
},
];
for (const scenario of scenarios) {
const pipeline = createClusterControlAdmissionPipeline(
options(scenario.override),
);
await assert.rejects(
pipeline.prepare(METADATA),
(error) =>
error instanceof ClusterControlAdmissionSecurityError &&
error.statusCode === 503 &&
error.code === scenario.code &&
!error.message.includes('database detail') &&
!error.message.includes('store detail'),
);
}
});
test('adapts the shared fenced Project Policy engine without an allow-all seam', async () => {
const policy = createClusterControlProjectPolicyAuthorizer({
async resolve(projectId, subject) {
assert.equal(projectId, 'prj_default');
return {
project: {
id: projectId,
name: 'Default',
slug: 'default',
status: 'active',
version: 4,
createdAtMs: 0,
updatedAtMs: 1,
},
binding: {
projectId,
subject,
version: 7,
state: 'active',
role: 'operator',
mutationId: 'grant-7',
changedBy: { type: 'user', id: 'usr_owner' },
createdAtMs: 1,
},
};
},
async append() {
throw new Error('not used');
},
});
assert.deepEqual(
await policy.authorize({
principal: PRINCIPAL,
operationId: 'run.create',
permission: 'run.start',
projectId: 'prj_default',
signal: METADATA.signal,
}),
{
effect: 'allow',
reasons: ['role_grant'],
fence: { projectVersion: 4, bindingVersion: 7 },
},
);
assert.equal(
(
await policy.authorize({
principal: PRINCIPAL,
operationId: 'project.update',
permission: 'project.manage',
projectId: null,
signal: METADATA.signal,
})
).effect,
'deny',
);
});
@@ -0,0 +1,268 @@
const assert = require('node:assert/strict');
const { chmod, mkdtemp, rm, writeFile } = require('node:fs/promises');
const { tmpdir } = require('node:os');
const { join } = require('node:path');
const { test } = require('node:test');
const {
ProductionClusterAiConfigError,
loadProductionClusterAiConfig,
startProductionClusterAiControlApplication,
} = require('@qinglong/cluster-control/ai-production');
const {
canonicalPluginPackagePromptOutputKeyringManifest,
PLUGIN_PACKAGE_PROMPT_OUTPUT_KEYRING_MANIFEST_SCHEMA,
} = require('@qinglong/ai/plugin-package-prompt-output-keyring-manifest');
function enabledEnvironment(overrides = {}) {
return {
QL3_CLUSTER_AI_ENABLED: 'true',
QL3_CLUSTER_AI_PROVIDER_AUTHORITY_FILE: '/var/run/qinglong/ai/providers.json',
QL3_CLUSTER_AI_SECRET_ROOT: '/var/run/qinglong/ai/provider-secrets',
...overrides,
};
}
function controlConfig() {
return {
enabled: true,
profile: 'cluster-control',
http: { host: '127.0.0.1', port: 5800 },
database: {
connection: {
host: '127.0.0.1',
port: 5432,
database: 'qinglong',
user: 'ql3_runtime',
password: 'test-only',
tls: { mode: 'disable' },
},
pool: { maxConnections: 8 },
},
security: { apiCredentialPepper: 'test-only' },
};
}
test('AI config is fail-closed and bounded behind the explicit process flag', () => {
assert.throws(
() => loadProductionClusterAiConfig({}),
(error) =>
error instanceof ProductionClusterAiConfigError &&
error.code === 'QL3_CLUSTER_AI_CONFIG_INVALID',
);
assert.deepEqual(loadProductionClusterAiConfig(enabledEnvironment()), {
enabled: true,
providerAuthorityFile: '/var/run/qinglong/ai/providers.json',
secretRootDirectory: '/var/run/qinglong/ai/provider-secrets',
maxConcurrent: 4,
recoveryLimit: 32,
databaseMaxConnections: 4,
});
assert.throws(
() =>
loadProductionClusterAiConfig(
enabledEnvironment({ QL3_CLUSTER_AI_MAX_CONCURRENT: '65' }),
),
/between 1 and 64/,
);
assert.throws(
() =>
loadProductionClusterAiConfig(
enabledEnvironment({
QL3_CLUSTER_AI_PROMPT_OUTPUT_ENABLED: 'true',
}),
),
/QL3_CLUSTER_AI_PROMPT_OUTPUT_KEYRING_ROOT is invalid/,
);
});
test('explicit AI composition injects one reviewed Prompt capability and drains it after HTTP control', async () => {
const secretRoot = await mkdtemp(join(tmpdir(), 'ql3-cluster-ai-secret-'));
const events = [];
const promptCatalog = Object.freeze({ inspect() {} });
const promptExecutions = Object.freeze({ execute() {} });
const promptExecutionInspections = Object.freeze({ inspectAuthorized() {} });
let promptOptions;
let controlOptions;
const neverUnavailable = new Promise(() => {});
try {
const application = await startProductionClusterAiControlApplication({
control: { config: controlConfig() },
ai: {
enabled: true,
providerAuthorityFile: '/unused/providers.json',
secretRootDirectory: secretRoot,
maxConcurrent: 3,
recoveryLimit: 11,
databaseMaxConnections: 2,
},
audit() {},
async bootstrapPrompt(options) {
promptOptions = options;
return {
status: 'active',
profile: 'cluster',
readiness: {},
capability: {},
prompts: {},
promptCatalog,
promptExecutions,
promptExecutionInspections,
async stop() {
events.push('stop-prompt');
return 'stopped';
},
};
},
async startControl(options) {
controlOptions = options;
return {
status: 'active',
address: { host: '127.0.0.1', port: 5800 },
evidence: {},
recovery: { safe: true, remaining: 0, failed: 0 },
unavailable: neverUnavailable,
availabilityStatus() {
return 'ready';
},
async stop() {
events.push('stop-control');
return 'stopped';
},
};
},
});
assert.equal(application.status, 'active');
assert.equal(application.availabilityStatus(), 'ready');
assert.equal(promptOptions.enabled, true);
assert.equal(promptOptions.maxConcurrent, 3);
assert.equal(promptOptions.recoveryLimit, 11);
assert.equal(controlOptions.promptCatalog.capability, promptCatalog);
assert.equal(controlOptions.promptExecution.capability, promptExecutions);
assert.equal(
controlOptions.promptExecutionInspection.capability,
promptExecutionInspections,
);
assert.equal('promptOutputRead' in controlOptions, false);
assert.equal(await application.stop(), 'stopped');
assert.equal(await application.stop(), 'stopped');
assert.deepEqual(events, ['stop-control', 'stop-prompt']);
} finally {
await rm(secretRoot, { recursive: true, force: true });
}
});
test('output-enabled AI composition wires exact and request-keyed protected reads', async () => {
const secretRoot = await mkdtemp(join(tmpdir(), 'ql3-cluster-ai-secret-'));
const outputRoot = await mkdtemp(join(tmpdir(), 'ql3-cluster-ai-output-'));
const manifest = canonicalPluginPackagePromptOutputKeyringManifest({
schema: PLUGIN_PACKAGE_PROMPT_OUTPUT_KEYRING_MANIFEST_SCHEMA,
generation: 1,
activeKeyId: 'prompt-key-1',
keys: { 'prompt-key-1': Buffer.alloc(32, 7).toString('base64url') },
retirements: {},
});
await writeFile(join(outputRoot, 'keyring.json'), manifest, { mode: 0o440 });
await chmod(join(outputRoot, 'keyring.json'), 0o440);
let promptOptions;
let controlOptions;
const promptOutputs = Object.freeze({ read() {} });
const promptExecutionOutputs = Object.freeze({ read() {} });
try {
const application = await startProductionClusterAiControlApplication({
control: { config: controlConfig() },
ai: {
enabled: true,
providerAuthorityFile: '/unused/providers.json',
secretRootDirectory: secretRoot,
promptOutputKeyringRootDirectory: outputRoot,
maxConcurrent: 1,
recoveryLimit: 1,
databaseMaxConnections: 1,
},
audit() {},
async bootstrapPrompt(options) {
promptOptions = options;
return {
status: 'active',
profile: 'cluster',
readiness: {},
capability: {},
prompts: {},
promptCatalog: { inspect() {} },
promptExecutions: { execute() {} },
promptExecutionInspections: { inspectAuthorized() {} },
promptOutputs,
promptExecutionOutputs,
async stop() { return 'stopped'; },
};
},
async startControl(options) {
controlOptions = options;
return {
status: 'active',
address: { host: '127.0.0.1', port: 5800 },
evidence: {},
recovery: { safe: true, remaining: 0, failed: 0 },
unavailable: new Promise(() => {}),
availabilityStatus() { return 'ready'; },
async stop() { return 'stopped'; },
};
},
});
assert.equal(typeof promptOptions.promptOutputKeys.resolve, 'function');
assert.equal(
typeof promptOptions.promptOutputRead.authorizer.authorize,
'function',
);
assert.equal(controlOptions.promptOutputRead.capability, promptOutputs);
assert.equal(
controlOptions.promptExecutionOutputRead.capability,
promptExecutionOutputs,
);
assert.equal(await application.stop(), 'stopped');
} finally {
manifest.fill(0);
await rm(secretRoot, { recursive: true, force: true });
await rm(outputRoot, { recursive: true, force: true });
}
});
test('AI composition fails closed and drains a non-active Prompt bootstrap', async () => {
const secretRoot = await mkdtemp(join(tmpdir(), 'ql3-cluster-ai-secret-'));
let stops = 0;
try {
await assert.rejects(
startProductionClusterAiControlApplication({
control: { config: controlConfig() },
ai: {
enabled: true,
providerAuthorityFile: '/unused/providers.json',
secretRootDirectory: secretRoot,
maxConcurrent: 1,
recoveryLimit: 1,
databaseMaxConnections: 1,
},
audit() {},
async bootstrapPrompt() {
return {
status: 'disabled',
profile: 'cluster',
async stop() {
stops += 1;
return 'stopped';
},
};
},
async startControl() {
throw new Error('control must not start');
},
}),
/Prompt application did not activate/,
);
assert.equal(stops, 1);
} finally {
await rm(secretRoot, { recursive: true, force: true });
}
});
@@ -0,0 +1,178 @@
const assert = require('node:assert/strict');
const { createHmac } = require('node:crypto');
const { test } = require('node:test');
const {
ApiCredentialUnavailableError,
} = require('@qinglong/runtime-core/api-credential');
const {
ClusterControlApiCredentialConfigurationError,
ClusterControlApiCredentialUnavailableError,
apiCredentialSecretDigest,
createClusterControlApiCredentialAuthenticator,
} = require('@qinglong/cluster-control/api-credential');
const NOW = 10_000;
const PEPPER = Buffer.alloc(32, 1).toString('base64url');
const SECRET = Buffer.alloc(32, 2).toString('base64url');
const CREDENTIAL_ID = 'app_primary';
function metadata(authorization = `Bearer ql3c_${CREDENTIAL_ID}_${SECRET}`) {
return {
requestId: 'request-1',
method: 'POST',
path: '/api/v3/projects/default/runs',
query: Object.freeze({}),
headers: Object.freeze({ authorization }),
signal: new AbortController().signal,
};
}
function credential(overrides = {}) {
return {
credentialId: CREDENTIAL_ID,
version: 2,
pepperKeyId: 'legacy-v1',
state: 'active',
subject: { type: 'api_app', id: 'app_primary' },
subjectStatus: 'active',
secretDigest: apiCredentialSecretDigest(PEPPER, CREDENTIAL_ID, SECRET),
createdAtMs: 1,
notBeforeAtMs: 1,
expiresAtMs: 100_000,
...overrides,
};
}
function authenticator(value = credential(), overrides = {}) {
return createClusterControlApiCredentialAuthenticator(
{
async resolve(credentialId) {
assert.equal(credentialId, CREDENTIAL_ID);
return value;
},
},
PEPPER,
{ now: () => NOW, ...overrides },
);
}
test('authenticates a high-entropy service bearer as a short-lived principal', async () => {
const principal = await authenticator().authenticate(metadata());
assert.deepEqual(principal, {
subject: { type: 'api_app', id: 'app_primary' },
authenticationId: 'api_credential:app_primary:2',
authenticatedAtMs: NOW,
expiresAtMs: NOW + 60_000,
assurance: 'service',
});
assert.equal(Object.isFrozen(principal), true);
assert.equal(JSON.stringify(principal).includes(SECRET), false);
});
test('derives a domain-separated HMAC digest and user assurance', async () => {
const expected = createHmac('sha256', Buffer.from(PEPPER, 'base64url'))
.update(Buffer.from('qinglong-api-credential-v1\0', 'utf8'))
.update(CREDENTIAL_ID, 'utf8')
.update('\0', 'utf8')
.update(Buffer.from(SECRET, 'base64url'))
.digest('hex');
assert.equal(
apiCredentialSecretDigest(PEPPER, CREDENTIAL_ID, SECRET),
expected,
);
const principal = await authenticator(
credential({ subject: { type: 'user', id: 'usr_primary' } }),
).authenticate(metadata());
assert.equal(principal.assurance, 'single_factor');
});
test('rejects missing, malformed, wrong, inactive and disabled credentials', async () => {
let repositoryCalls = 0;
const strict = createClusterControlApiCredentialAuthenticator(
{
async resolve() {
repositoryCalls += 1;
return credential();
},
},
PEPPER,
{ now: () => NOW },
);
for (const header of [
undefined,
'bearer token',
`Bearer ql3c_${CREDENTIAL_ID}_short`,
`Bearer ql3c_${CREDENTIAL_ID}_${Buffer.alloc(32, 3).toString('base64url')}`,
]) {
const request = metadata();
request.headers = Object.freeze(
header === undefined ? {} : { authorization: header },
);
assert.equal(await strict.authenticate(request), null);
}
assert.equal(
repositoryCalls,
1,
'only a structurally valid token reaches SQL',
);
for (const value of [
credential({ state: 'revoked' }),
credential({ subjectStatus: 'disabled' }),
credential({ notBeforeAtMs: NOW + 1 }),
credential({ expiresAtMs: NOW }),
null,
]) {
assert.equal(await authenticator(value).authenticate(metadata()), null);
}
});
test('maps storage, corrupt record and cancellation failures to unavailable', async () => {
const unavailable = createClusterControlApiCredentialAuthenticator(
{
async resolve() {
throw new ApiCredentialUnavailableError();
},
},
PEPPER,
{ now: () => NOW },
);
await assert.rejects(
unavailable.authenticate(metadata()),
ClusterControlApiCredentialUnavailableError,
);
await assert.rejects(
authenticator(credential({ secretDigest: 'corrupt' })).authenticate(
metadata(),
),
ClusterControlApiCredentialUnavailableError,
);
await assert.rejects(
authenticator(credential({ pepperKeyId: 'other-v1' })).authenticate(
metadata(),
),
ClusterControlApiCredentialUnavailableError,
);
const controller = new AbortController();
controller.abort();
await assert.rejects(
authenticator().authenticate({ ...metadata(), signal: controller.signal }),
ClusterControlApiCredentialUnavailableError,
);
});
test('rejects weak pepper and unbounded principal lifetime at construction', () => {
const repository = { async resolve() {} };
assert.throws(
() => createClusterControlApiCredentialAuthenticator(repository, 'weak'),
ClusterControlApiCredentialConfigurationError,
);
assert.throws(
() =>
createClusterControlApiCredentialAuthenticator(repository, PEPPER, {
principalTtlMs: 300_001,
}),
ClusterControlApiCredentialConfigurationError,
);
});
@@ -0,0 +1,787 @@
const assert = require('node:assert/strict');
const http = require('node:http');
const net = require('node:net');
const { test } = require('node:test');
const {
startClusterControlApplication,
} = require('@qinglong/cluster-control/application');
const {
ClusterControlAvailabilityFence,
} = require('@qinglong/cluster-control/availability');
const {
apiCredentialSecretDigest,
} = require('@qinglong/cluster-control/api-credential');
const {
createClusterControlAdmissionPipeline,
createClusterControlProjectPolicyAuthorizer,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
const {
PostgresSchemaReadinessError,
postgresqlMainMigrationManifest,
} = require('@qinglong/cluster-postgres/runtime');
const {
postgresqlControlSchemaContract,
} = require('@qinglong/cluster-postgres');
function freePort() {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.once('error', reject);
server.listen(0, '127.0.0.1', () => {
const address = server.address();
server.close((error) => {
if (error) reject(error);
else resolve(address.port);
});
});
});
}
function request(port, path, options = {}) {
const body =
options.body === undefined
? undefined
: Buffer.from(JSON.stringify(options.body));
return new Promise((resolve, reject) => {
const outgoing = http.request(
{
host: '127.0.0.1',
port,
path,
method: options.method ?? 'GET',
headers: {
connection: 'close',
...(body
? {
'content-type': 'application/json',
'content-length': String(body.byteLength),
}
: {}),
...options.headers,
},
},
(response) => {
const chunks = [];
response.on('data', (chunk) => chunks.push(chunk));
response.on('end', () => {
const text = Buffer.concat(chunks).toString('utf8');
resolve({
statusCode: response.statusCode,
body: text.length === 0 ? null : JSON.parse(text),
});
});
},
);
outgoing.once('error', reject);
if (body) outgoing.write(body);
outgoing.end();
});
}
function migrationHistory() {
return postgresqlMainMigrationManifest.migrations.map((migration, index) => ({
streamId: postgresqlMainMigrationManifest.id,
dialect: postgresqlMainMigrationManifest.dialect,
migrationId: migration.id,
checksum: migration.checksum,
appliedAtMs: index + 1,
}));
}
function admission(handler) {
return {
async prepare(metadata) {
return {
handle(body) {
return handler({ ...metadata, body });
},
};
},
};
}
function runtimePrivileges() {
const privileges = {
schema_migrations: [true, false, false, false],
schema_capabilities: [true, false, false, false],
projects: [true, true, true, false],
task_definitions: [true, false, false, false],
task_definition_revisions: [true, false, false, false],
task_execution_revisions: [true, false, false, false],
triggers: [true, false, false, false],
trigger_revisions: [true, false, false, false],
trigger_schedules: [true, false, true, false],
project_role_bindings: [true, true, false, false],
identity_subjects: [true, false, false, false],
api_credentials: [true, false, false, false],
security_audit_events: [false, true, false, false],
identity_subject_mutations: [false, false, false, false],
api_credential_mutations: [false, false, false, false],
runs: [true, true, true, false],
step_runs: [true, true, true, false],
step_run_mutations: [true, true, false, false],
tool_execution_trace_anchors: [true, true, false, false],
tool_execution_audit_receipts: [true, true, false, false],
tool_execution_start_barriers: [true, true, false, false],
tool_execution_start_artifact_bindings: [true, true, false, false],
tool_execution_completions: [true, true, false, false],
tool_execution_failure_completions: [true, true, false, false],
tool_result_key_catalog_generations: [true, false, false, false],
tool_execution_result_key_bindings: [true, true, false, false],
tool_execution_result_rekey_overlays: [true, false, false, false],
tool_execution_result_rekey_heads: [true, false, false, false],
tool_result_key_retirement_receipts: [false, false, false, false],
tool_invocation_input_artifacts: [true, true, false, false],
tool_invocation_preview_artifacts: [true, true, false, false],
run_attempts: [true, true, true, false],
run_recovery_controls: [true, true, true, false],
worker_sessions: [true, true, true, false],
run_dispatch_leases: [true, true, true, false],
worker_credentials: [false, false, false, false],
worker_credential_mutations: [false, false, false, false],
worker_credential_deliveries: [false, false, false, false],
worker_credential_stage_discards: [false, false, false, false],
worker_credential_management_plans: [false, false, false, false],
worker_credential_management_quota_buckets: [false, false, false, false],
plugin_package_installs: [false, false, false, false],
plugin_package_install_heads: [false, false, false, false],
plugin_package_install_mutations: [false, false, false, false],
approval_requests: [false, false, false, false],
approved_action_dispatches: [false, false, false, false],
approved_action_executions: [false, false, false, false],
plugin_package_install_proposals: [false, false, false, false],
plugin_package_admission_receipts: [false, false, false, false],
plugin_package_management_quota_buckets: [false, false, false, false],
plugin_package_identity_keyset_ledger: [false, false, false, false],
plugin_package_materialized_revisions: [false, false, false, false],
plugin_package_task_ownerships: [false, false, false, false],
plugin_package_task_reconciliations: [false, false, false, false],
plugin_package_task_reconciliation_items: [false, false, false, false],
project_tool_definition_snapshots: [false, false, false, false],
project_tool_definition_snapshot_sources: [false, false, false, false],
plugin_package_publisher_provenance: [false, false, false, false],
plugin_package_publisher_revocation_impact_items: [
false,
false,
false,
false,
],
plugin_package_publisher_revocation_impacts: [false, false, false, false],
plugin_package_publisher_revocation_proposals: [false, false, false, false],
plugin_package_publisher_revocation_receipts: [false, false, false, false],
plugin_package_publisher_trust_heads: [false, false, false, false],
plugin_package_publisher_trust_snapshots: [false, false, false, false],
plugin_package_publisher_trust_transition_proposals: [
false,
false,
false,
false,
],
plugin_package_publisher_trust_transition_receipts: [
false,
false,
false,
false,
],
plugin_package_quarantine_events: [false, false, false, false],
plugin_package_withdrawal_receipts: [false, false, false, false],
plugin_package_withdrawal_tasks: [false, false, false, false],
plugin_package_lifecycle_events: [false, false, false, false],
plugin_package_lifecycle_heads: [false, false, false, false],
plugin_package_lifecycle_receipts: [false, false, false, false],
plugin_package_lifecycle_tasks: [false, false, false, false],
plugin_package_lifecycle_plans: [false, false, false, false],
plugin_package_automation_publications: [true, false, false, false],
plugin_package_automation_publication_heads: [true, false, false, false],
plugin_package_workflow_admissions: [true, true, false, false],
plugin_package_workflow_admission_steps: [true, true, false, false],
plugin_package_workflow_task_attempt_admissions: [
true,
true,
false,
false,
],
worker_execution_attestations: [true, false, false, false],
run_events: [true, true, false, false],
run_retry_policies: [true, true, true, false],
};
return Object.entries(privileges).map(
([
tableName,
[selectAllowed, insertAllowed, updateAllowed, deleteAllowed],
]) => ({
tableName,
selectAllowed,
insertAllowed,
updateAllowed,
deleteAllowed,
isOwner: false,
}),
);
}
function databaseResource(events, options = {}) {
const contract = postgresqlControlSchemaContract;
let firstQuery = true;
const pool = {
async query(text, values) {
events.push('query');
if (firstQuery && options.readinessGate) {
firstQuery = false;
options.onReadinessQuery?.();
await options.readinessGate;
}
if (text.includes("current_setting('server_version_num')")) {
return {
rows: [
{
serverVersionNum: options.serverVersionNum ?? '160014',
currentUser: 'ql3_runtime',
inRecovery: false,
transactionReadOnly: 'off',
},
],
};
}
if (text.includes('FROM "ql3"."schema_migrations"')) {
return { rows: migrationHistory() };
}
if (text.includes('FROM "ql3"."schema_capabilities"')) {
return {
rows: [
{
contractName: contract.contractName,
contractVersion: contract.contractVersion,
migrationId: contract.migrationId,
capabilities: contract.capabilities,
},
],
};
}
if (text.includes('FROM pg_class tables')) {
return {
rows: contract.tables.flatMap((table) =>
table.columns.map((columnName) => ({
tableName: table.name,
columnName,
})),
),
};
}
if (text.includes('FROM pg_indexes')) {
return { rows: contract.indexes.map((indexName) => ({ indexName })) };
}
if (text.includes('FROM pg_constraint')) {
return {
rows: [
...contract.checks.map((constraintName) => ({
constraintName,
constraintType: 'check',
})),
...contract.foreignKeys.map((constraintName) => ({
constraintName,
constraintType: 'foreign_key',
})),
],
};
}
if (text.includes('FROM pg_proc routines')) {
return {
rows: contract.functions.map((definition) => ({
functionName: definition.name,
identityArguments: definition.identityArguments,
owner: definition.owner,
securityDefiner: definition.securityDefiner,
volatility: definition.volatility,
configuration: definition.configuration,
publicExecute: false,
})),
};
}
if (text.includes('FROM pg_catalog.pg_roles')) {
return {
rows: [
{
canLogin: true,
superuser: false,
createDatabase: false,
createRole: false,
replication: false,
bypassRowLevelSecurity: false,
databaseConnect: true,
},
],
};
}
if (text.includes('has_schema_privilege')) {
return { rows: [{ schemaUsage: true, schemaCreate: false }] };
}
if (text.includes('has_table_privilege')) {
return { rows: runtimePrivileges() };
}
if (text.includes('has_function_privilege')) {
return {
rows: contract.functions.map(({ name: functionName }) => ({
functionName,
executeAllowed: [
'plugin_package_automation_start_allowed',
'plugin_package_workflow_admission_snapshot',
'plugin_package_workflow_task_attempt_snapshot',
'plugin_package_run_start_allowed',
'plugin_package_tool_start_allowed',
].includes(functionName),
isOwner: false,
})),
};
}
if (text.includes('WITH observation AS')) {
return {
rows: options.recoveryRows ?? [
{
observedAtMs: '1',
kind: null,
id: null,
runId: null,
status: null,
createdAtMs: null,
},
],
};
}
if (options.query) return options.query(text, values);
throw new Error(`unexpected query: ${text}`);
},
async connect() {
throw new Error('Repository connections are not used by this fixture');
},
};
return {
pool,
async close() {
events.push('close-database');
},
};
}
function baseOptions(events, port, overrides = {}) {
return {
enabled: true,
profile: 'cluster-control',
apiCredentialPepper: 'A'.repeat(43),
recovery: { ownerId: 'test-replica', providers: [] },
availability: new ClusterControlAvailabilityFence(),
http: { host: '127.0.0.1', port, drainTimeoutMs: 1000 },
async openDatabase() {
events.push('open-database');
return databaseResource(events);
},
create({
evidence,
authenticator,
policies,
runs,
taskDefinitions,
taskExecutionRevisions,
triggers,
schedules,
securityAudit,
}) {
events.push('create-stack');
assert.equal(
evidence.contractVersion,
postgresqlControlSchemaContract.contractVersion,
);
assert.equal(typeof authenticator.authenticate, 'function');
assert.equal(typeof policies.resolve, 'function');
assert.equal(typeof runs.transaction, 'function');
assert.equal(
typeof taskDefinitions.findCurrentTaskDefinition,
'function',
);
assert.equal(
typeof taskExecutionRevisions.resolveClusterTaskExecutionRevision,
'function',
);
assert.equal(typeof triggers.findCurrentTrigger, 'function');
assert.equal(typeof schedules.claimNextClusterSchedule, 'function');
assert.equal(typeof schedules.commitClusterScheduleDecision, 'function');
assert.equal(typeof securityAudit.record, 'function');
return {
async reconcile() {
events.push('reconcile');
return { safe: true, remaining: 0, failed: 0 };
},
async startLifecycles() {
events.push('start-lifecycles');
return true;
},
admission: admission(async (incoming) => {
events.push(`handle:${incoming.path}`);
return { statusCode: 202, body: { accepted: true } };
}),
async stop() {
events.push('stop-stack');
return 'stopped';
},
};
},
audit(record) {
events.push(`audit:${record.state}`);
},
...overrides,
};
}
test('disabled and wrong-profile applications never bind or open PostgreSQL', async () => {
const disabledEvents = [];
const disabled = await startClusterControlApplication(
baseOptions(disabledEvents, 0, { enabled: false }),
);
assert.equal(disabled.status, 'disabled');
assert.deepEqual(disabledEvents, ['audit:disabled']);
const wrongProfileEvents = [];
await assert.rejects(
startClusterControlApplication(
baseOptions(wrongProfileEvents, 0, { profile: 'standalone' }),
),
/cannot activate cluster-control/,
);
assert.deepEqual(wrongProfileEvents, []);
});
test('rejects an invalid credential pepper before binding or opening PostgreSQL', async () => {
const events = [];
await assert.rejects(
startClusterControlApplication(
baseOptions(events, 0, { apiCredentialPepper: undefined }),
),
/API credential configuration is invalid/,
);
assert.deepEqual(events, []);
});
test('rejects an enabled application without an availability source', async () => {
const events = [];
await assert.rejects(
startClusterControlApplication(
baseOptions(events, 0, { availability: undefined }),
),
/availability source is invalid/,
);
assert.deepEqual(events, []);
});
test('composes bearer authentication, fenced Policy and durable audit on one Pool', async (t) => {
const pepper = 'A'.repeat(43);
const secret = Buffer.alloc(32, 2).toString('base64url');
const digest = apiCredentialSecretDigest(pepper, 'app_primary', secret);
const auditWrites = [];
const events = [];
const port = await freePort();
const result = await startClusterControlApplication(
baseOptions(events, port, {
apiCredentialPepper: pepper,
async openDatabase() {
events.push('open-database');
return databaseResource(events, {
async query(text, values) {
if (text.includes('FROM "ql3"."api_credentials"')) {
return {
rows: [
{
credentialId: 'app_primary',
version: '1',
state: 'active',
subjectType: 'api_app',
subjectId: 'app_primary',
subjectStatus: 'active',
pepperKeyId: 'legacy-v1',
secretDigest: digest,
createdAtMs: '1',
notBeforeAtMs: '1',
expiresAtMs: String(Date.now() + 60_000),
},
],
};
}
if (text.includes('FROM "ql3"."projects" AS project')) {
return {
rows: [
{
projectId: 'default',
projectName: 'Default',
projectSlug: 'default',
projectStatus: 'active',
projectVersion: '2',
projectCreatedAtMs: '0',
projectUpdatedAtMs: '1',
bindingProjectId: 'default',
bindingSubjectType: 'api_app',
bindingSubjectId: 'app_primary',
bindingVersion: '3',
bindingState: 'active',
bindingRole: 'operator',
bindingMutationId: 'grant-app',
bindingChangedByType: 'user',
bindingChangedById: 'usr_owner',
bindingCreatedAtMs: '1',
},
],
};
}
if (text.startsWith('INSERT INTO "ql3"."security_audit_events"')) {
auditWrites.push(values);
return { rows: [] };
}
throw new Error(`unexpected repository query: ${text}`);
},
});
},
create({ authenticator, policies, securityAudit }) {
events.push('create-stack');
return {
async reconcile() {
return { safe: true, remaining: 0, failed: 0 };
},
async startLifecycles() {
return true;
},
admission: createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([
{
method: 'POST',
path: '/api/v3/projects/{projectId}/runs',
operationId: 'run.create',
permission: 'run.start',
projectParameter: 'projectId',
handle(input) {
return {
statusCode: 202,
body: {
accepted: true,
subject: input.principal.subject.id,
},
};
},
},
]),
authenticator,
policy: createClusterControlProjectPolicyAuthorizer(policies),
audit: securityAudit,
}),
async stop() {
return 'stopped';
},
};
},
}),
);
t.after(() => result.stop());
const rejected = await request(port, '/api/v3/projects/default/runs', {
method: 'POST',
headers: {
'content-type': 'application/json',
'content-length': String(1024 * 1024),
},
});
assert.equal(rejected.statusCode, 401);
assert.equal(auditWrites.length, 1);
assert.equal(auditWrites[0][7], 'authentication_rejected');
const accepted = await request(port, '/api/v3/projects/default/runs', {
method: 'POST',
headers: {
authorization: `Bearer ql3c_app_primary_${secret}`,
},
body: { taskId: 'task-1' },
});
assert.equal(accepted.statusCode, 202);
assert.deepEqual(accepted.body, {
accepted: true,
subject: 'app_primary',
});
assert.equal(auditWrites.length, 2);
assert.equal(auditWrites[1][7], 'allowed');
assert.equal(auditWrites[1][9], 2);
assert.equal(auditWrites[1][10], 3);
assert.equal(JSON.stringify(auditWrites).includes(secret), false);
});
test('serves not-ready while auditing PostgreSQL, then opens admission', async () => {
const events = [];
const port = await freePort();
let releaseReadiness;
const readinessGate = new Promise((resolve) => {
releaseReadiness = resolve;
});
let readinessStarted;
const readinessStartedPromise = new Promise((resolve) => {
readinessStarted = resolve;
});
const starting = startClusterControlApplication(
baseOptions(events, port, {
async openDatabase() {
events.push('open-database');
return databaseResource(events, {
readinessGate,
onReadinessQuery: readinessStarted,
});
},
}),
);
await readinessStartedPromise;
assert.equal((await request(port, '/livez')).statusCode, 200);
assert.deepEqual(await request(port, '/readyz'), {
statusCode: 503,
body: { status: 'not_ready' },
});
assert.equal(events.includes('create-stack'), false);
releaseReadiness();
const application = await starting;
assert.equal(application.status, 'active');
assert.deepEqual(await request(port, '/readyz'), {
statusCode: 200,
body: { status: 'ready' },
});
assert.deepEqual(
await request(port, '/api/v3/runs', {
method: 'POST',
body: { taskId: 'task-1' },
}),
{ statusCode: 202, body: { accepted: true } },
);
assert.equal(await application.stop(), 'stopped');
assert.deepEqual(events.slice(-3), [
'stop-stack',
'audit:stopped',
'close-database',
]);
await assert.rejects(request(port, '/livez'));
});
test('withdraws and drains application admission before stack and Pool stop', async () => {
const events = [];
const port = await freePort();
let entered;
const enteredPromise = new Promise((resolve) => {
entered = resolve;
});
let release;
const handlerGate = new Promise((resolve) => {
release = resolve;
});
const application = await startClusterControlApplication(
baseOptions(events, port, {
create(input) {
const stack = baseOptions([], port).create(input);
return {
...stack,
admission: admission(async () => {
events.push('handle:slow');
entered();
await handlerGate;
events.push('handler-finished');
return { statusCode: 200, body: { completed: true } };
}),
async stop() {
events.push('stop-stack');
return 'stopped';
},
};
},
}),
);
const admitted = request(port, '/api/v3/slow');
await enteredPromise;
const stopping = application.stop();
assert.deepEqual(await request(port, '/readyz'), {
statusCode: 503,
body: { status: 'not_ready' },
});
assert.equal(events.includes('stop-stack'), false);
release();
assert.deepEqual(await admitted, {
statusCode: 503,
body: { code: 'admission_draining' },
});
assert.equal(await stopping, 'stopped');
assert.ok(events.indexOf('handler-finished') < events.indexOf('stop-stack'));
assert.ok(events.indexOf('stop-stack') < events.indexOf('close-database'));
});
test('a database availability signal withdraws admission but keeps liveness', async () => {
const events = [];
const port = await freePort();
const availability = new ClusterControlAvailabilityFence();
const application = await startClusterControlApplication(
baseOptions(events, port, { availability }),
);
assert.equal(application.availabilityStatus(), 'ready');
assert.equal((await request(port, '/readyz')).statusCode, 200);
const unavailable = new Error('PostgreSQL connection lost');
assert.equal(await availability.signal(unavailable), 'signaled');
assert.equal(await application.unavailable, unavailable);
assert.equal(application.availabilityStatus(), 'unavailable');
assert.deepEqual(await request(port, '/readyz'), {
statusCode: 503,
body: { status: 'not_ready' },
});
assert.equal((await request(port, '/livez')).statusCode, 200);
assert.deepEqual(
await request(port, '/api/v3/runs', {
method: 'POST',
body: { taskId: 'task-1' },
}),
{ statusCode: 503, body: { code: 'not_ready' } },
);
assert.ok(events.indexOf('stop-stack') < events.indexOf('close-database'));
assert.equal(await application.stop(), 'stopped');
assert.equal(application.availabilityStatus(), 'stopped');
await assert.rejects(request(port, '/livez'));
});
test('an availability signal raised before startup cannot open admission', async () => {
const events = [];
const port = await freePort();
const availability = new ClusterControlAvailabilityFence();
assert.equal(
await availability.signal(new Error('PostgreSQL unavailable at startup')),
'signaled',
);
await assert.rejects(
startClusterControlApplication(baseOptions(events, port, { availability })),
(error) => error?.code === 'CLUSTER_CONTROL_DATABASE_UNAVAILABLE',
);
assert.ok(events.indexOf('stop-stack') < events.indexOf('close-database'));
await assert.rejects(request(port, '/livez'));
});
test('readiness failure closes both PostgreSQL and the probe listener', async () => {
const events = [];
const port = await freePort();
await assert.rejects(
startClusterControlApplication(
baseOptions(events, port, {
async openDatabase() {
events.push('open-database');
return databaseResource(events, { serverVersionNum: '150018' });
},
}),
),
(error) =>
error instanceof PostgresSchemaReadinessError &&
error.code === 'server_version_unsupported',
);
assert.equal(events.includes('create-stack'), false);
assert.deepEqual(events.slice(-2), ['audit:failed', 'close-database']);
await assert.rejects(request(port, '/livez'));
});
@@ -0,0 +1,120 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
createClusterControlAuthenticationShield,
} = require('../dist/authentication/authenticationShield.js');
function shield(overrides = {}) {
let currentTime = 1000;
const instance = createClusterControlAuthenticationShield({
windowMs: 1000,
maxRequestsPerPeer: 1,
maxRequestsGlobal: 3,
maxTrackedPeers: 2,
now: () => currentTime,
...overrides,
});
return {
instance,
advance(milliseconds) {
currentTime += milliseconds;
},
setTime(value) {
currentTime = value;
},
};
}
function assertAllowed(result) {
assert.equal(result.allowed, true);
assert.equal(typeof result.refund, 'function');
return result;
}
test('bounds per-peer and aggregate attempts in a process-local window', () => {
const { instance } = shield();
assertAllowed(instance.consume('192.0.2.10'));
assert.deepEqual(instance.consume('192.0.2.10'), {
allowed: false,
reason: 'peer',
retryAfterMs: 1000,
});
assertAllowed(instance.consume('192.0.2.11'));
assert.deepEqual(instance.consume('192.0.2.12'), {
allowed: false,
reason: 'global',
retryAfterMs: 1000,
});
instance.close();
});
test('keeps peer state bounded and reclaims expired windows lazily', () => {
const clock = shield({ maxRequestsGlobal: 20 });
assertAllowed(clock.instance.consume('192.0.2.10'));
assertAllowed(clock.instance.consume('192.0.2.11'));
assert.deepEqual(clock.instance.consume('192.0.2.12'), {
allowed: false,
reason: 'capacity',
retryAfterMs: 1000,
});
clock.advance(1000);
assertAllowed(clock.instance.consume('192.0.2.12'));
clock.instance.close();
});
test('fails closed on a broken monotonic clock and after disposal', () => {
const clock = shield({ maxRequestsPerPeer: 20, maxRequestsGlobal: 20 });
assertAllowed(clock.instance.consume(undefined));
clock.setTime(999);
assert.deepEqual(clock.instance.consume(undefined), {
allowed: false,
reason: 'clock',
retryAfterMs: 1000,
});
clock.instance.close();
assert.deepEqual(clock.instance.consume('192.0.2.10'), {
allowed: false,
reason: 'clock',
retryAfterMs: 1000,
});
});
test('refunds only the exact successful authentication attempt once', () => {
const { instance } = shield({
maxRequestsPerPeer: 1,
maxRequestsGlobal: 1,
});
const first = assertAllowed(instance.consume('192.0.2.10'));
assert.deepEqual(instance.consume('192.0.2.10'), {
allowed: false,
reason: 'global',
retryAfterMs: 1000,
});
first.refund();
first.refund();
const second = assertAllowed(instance.consume('192.0.2.10'));
second.refund();
assertAllowed(instance.consume('192.0.2.11'));
instance.close();
});
test('a delayed refund cannot alter a replacement window', () => {
const clock = shield({
maxRequestsPerPeer: 1,
maxRequestsGlobal: 1,
});
const expired = assertAllowed(clock.instance.consume('192.0.2.10'));
clock.advance(1000);
assertAllowed(clock.instance.consume('192.0.2.10'));
expired.refund();
assert.deepEqual(clock.instance.consume('192.0.2.11'), {
allowed: false,
reason: 'global',
retryAfterMs: 1000,
});
clock.instance.close();
});
@@ -0,0 +1,40 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterControlAvailabilityFence,
} = require('@qinglong/cluster-control/availability');
test('signals one bound listener exactly once without a retry path', async () => {
const fence = new ClusterControlAvailabilityFence();
const reasons = [];
const unsubscribe = fence.subscribe(async (error) => {
reasons.push(error.message);
});
assert.equal(fence.status, 'available');
assert.equal(await fence.signal(new Error('connection lost')), 'signaled');
assert.equal(fence.status, 'unavailable');
assert.equal(
await fence.signal(new Error('second connection lost')),
'already_unavailable',
);
assert.deepEqual(reasons, ['connection lost']);
unsubscribe();
unsubscribe();
fence.dispose();
assert.equal(fence.status, 'disposed');
assert.equal(await fence.signal(new Error('after dispose')), 'disposed');
});
test('delivers an early signal when the single application listener binds', async () => {
const fence = new ClusterControlAvailabilityFence();
assert.equal(await fence.signal(new Error('early failure')), 'signaled');
let reason;
fence.subscribe((error) => {
reason = error.message;
});
await Promise.resolve();
assert.equal(reason, 'early failure');
assert.throws(() => fence.subscribe(() => {}), /already bound/);
await assert.rejects(() => fence.signal('invalid'), /error is invalid/);
});
@@ -0,0 +1,753 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
PostgresSchemaReadinessError,
postgresqlControlSchemaContract,
postgresqlMainMigrationStream,
} = require('@qinglong/cluster-postgres');
const { bootstrapClusterControlRuntime } = require('@qinglong/cluster-control');
function migrationHistory() {
return postgresqlMainMigrationStream.migrations.map((migration, index) => ({
streamId: 'postgresql-main',
dialect: 'postgresql',
migrationId: migration.id,
checksum: migration.checksum,
appliedAtMs: index + 1,
}));
}
function runtimePrivileges() {
const privileges = {
schema_migrations: [true, false, false, false],
schema_capabilities: [true, false, false, false],
projects: [true, true, true, false],
task_definitions: [true, false, false, false],
task_definition_revisions: [true, false, false, false],
task_execution_revisions: [true, false, false, false],
triggers: [true, false, false, false],
trigger_revisions: [true, false, false, false],
trigger_schedules: [true, false, true, false],
project_role_bindings: [true, true, false, false],
identity_subjects: [true, false, false, false],
api_credentials: [true, false, false, false],
security_audit_events: [false, true, false, false],
identity_subject_mutations: [false, false, false, false],
api_credential_mutations: [false, false, false, false],
runs: [true, true, true, false],
step_runs: [true, true, true, false],
step_run_mutations: [true, true, false, false],
tool_execution_trace_anchors: [true, true, false, false],
tool_execution_audit_receipts: [true, true, false, false],
tool_execution_start_barriers: [true, true, false, false],
tool_execution_start_artifact_bindings: [true, true, false, false],
tool_execution_completions: [true, true, false, false],
tool_execution_failure_completions: [true, true, false, false],
tool_result_key_catalog_generations: [true, false, false, false],
tool_execution_result_key_bindings: [true, true, false, false],
tool_execution_result_rekey_overlays: [true, false, false, false],
tool_execution_result_rekey_heads: [true, false, false, false],
tool_result_key_retirement_receipts: [false, false, false, false],
tool_invocation_input_artifacts: [true, true, false, false],
tool_invocation_preview_artifacts: [true, true, false, false],
run_attempts: [true, true, true, false],
run_recovery_controls: [true, true, true, false],
worker_sessions: [true, true, true, false],
run_dispatch_leases: [true, true, true, false],
worker_credentials: [false, false, false, false],
worker_credential_mutations: [false, false, false, false],
worker_credential_deliveries: [false, false, false, false],
worker_credential_stage_discards: [false, false, false, false],
worker_credential_management_plans: [false, false, false, false],
worker_credential_management_quota_buckets: [false, false, false, false],
plugin_package_installs: [false, false, false, false],
plugin_package_install_heads: [false, false, false, false],
plugin_package_install_mutations: [false, false, false, false],
approval_requests: [false, false, false, false],
approved_action_dispatches: [false, false, false, false],
approved_action_executions: [false, false, false, false],
plugin_package_install_proposals: [false, false, false, false],
plugin_package_admission_receipts: [false, false, false, false],
plugin_package_management_quota_buckets: [false, false, false, false],
plugin_package_identity_keyset_ledger: [false, false, false, false],
plugin_package_materialized_revisions: [false, false, false, false],
plugin_package_task_ownerships: [false, false, false, false],
plugin_package_task_reconciliations: [false, false, false, false],
plugin_package_task_reconciliation_items: [false, false, false, false],
project_tool_definition_snapshots: [false, false, false, false],
project_tool_definition_snapshot_sources: [false, false, false, false],
plugin_package_publisher_provenance: [false, false, false, false],
plugin_package_publisher_revocation_impact_items: [
false,
false,
false,
false,
],
plugin_package_publisher_revocation_impacts: [false, false, false, false],
plugin_package_publisher_revocation_proposals: [false, false, false, false],
plugin_package_publisher_revocation_receipts: [false, false, false, false],
plugin_package_publisher_trust_heads: [false, false, false, false],
plugin_package_publisher_trust_snapshots: [false, false, false, false],
plugin_package_publisher_trust_transition_proposals: [
false,
false,
false,
false,
],
plugin_package_publisher_trust_transition_receipts: [
false,
false,
false,
false,
],
plugin_package_quarantine_events: [false, false, false, false],
plugin_package_withdrawal_receipts: [false, false, false, false],
plugin_package_withdrawal_tasks: [false, false, false, false],
plugin_package_lifecycle_events: [false, false, false, false],
plugin_package_lifecycle_heads: [false, false, false, false],
plugin_package_lifecycle_receipts: [false, false, false, false],
plugin_package_lifecycle_tasks: [false, false, false, false],
plugin_package_lifecycle_plans: [false, false, false, false],
plugin_package_automation_publications: [true, false, false, false],
plugin_package_automation_publication_heads: [true, false, false, false],
plugin_package_workflow_admissions: [true, true, false, false],
plugin_package_workflow_admission_steps: [true, true, false, false],
plugin_package_workflow_task_attempt_admissions: [true, true, false, false],
worker_execution_attestations: [true, false, false, false],
run_events: [true, true, false, false],
run_retry_policies: [true, true, true, false],
};
return Object.entries(privileges).map(
([
tableName,
[selectAllowed, insertAllowed, updateAllowed, deleteAllowed],
]) => ({
tableName,
selectAllowed,
insertAllowed,
updateAllowed,
deleteAllowed,
isOwner: false,
}),
);
}
function databaseResource(events, overrides = {}) {
const contract = postgresqlControlSchemaContract;
const pool = {
async query(text) {
events.push(
`query:${
events.filter((event) => event.startsWith('query:')).length + 1
}`,
);
if (text.includes("current_setting('server_version_num')")) {
return {
rows: [
{
serverVersionNum: overrides.serverVersionNum ?? '160014',
currentUser: 'ql3_runtime',
inRecovery: false,
transactionReadOnly: 'off',
},
],
};
}
if (text.includes('FROM "ql3"."schema_migrations"')) {
return { rows: migrationHistory() };
}
if (text.includes('FROM "ql3"."schema_capabilities"')) {
return {
rows: [
{
contractName: contract.contractName,
contractVersion: contract.contractVersion,
migrationId: contract.migrationId,
capabilities: contract.capabilities,
},
],
};
}
if (text.includes('FROM pg_class tables')) {
return {
rows: contract.tables.flatMap((table) =>
table.columns.map((columnName) => ({
tableName: table.name,
columnName,
})),
),
};
}
if (text.includes('FROM pg_indexes')) {
return {
rows: contract.indexes.map((indexName) => ({ indexName })),
};
}
if (text.includes('FROM pg_constraint')) {
return {
rows: [
...contract.checks.map((constraintName) => ({
constraintName,
constraintType: 'check',
})),
...contract.foreignKeys.map((constraintName) => ({
constraintName,
constraintType: 'foreign_key',
})),
],
};
}
if (text.includes('FROM pg_proc routines')) {
return {
rows: contract.functions.map((definition) => ({
functionName: definition.name,
identityArguments: definition.identityArguments,
owner: definition.owner,
securityDefiner: definition.securityDefiner,
volatility: definition.volatility,
configuration: definition.configuration,
publicExecute: false,
})),
};
}
if (text.includes('FROM pg_catalog.pg_roles')) {
return {
rows: [
{
canLogin: true,
superuser: false,
createDatabase: false,
createRole: false,
replication: false,
bypassRowLevelSecurity: false,
databaseConnect: true,
},
],
};
}
if (text.includes('has_schema_privilege')) {
return { rows: [{ schemaUsage: true, schemaCreate: false }] };
}
if (text.includes('has_table_privilege')) {
return { rows: runtimePrivileges() };
}
if (text.includes('has_function_privilege')) {
return {
rows: contract.functions.map(({ name: functionName }) => ({
functionName,
executeAllowed: [
'plugin_package_automation_start_allowed',
'plugin_package_workflow_admission_snapshot',
'plugin_package_workflow_task_attempt_snapshot',
'plugin_package_run_start_allowed',
'plugin_package_tool_start_allowed',
].includes(functionName),
isOwner: false,
})),
};
}
if (
overrides.runtimeScans &&
text.includes('JOIN LATERAL') &&
text.includes('"ql3"."run_retry_policies"')
) {
events.push('scan:lost-retry');
return { rows: [], rowCount: 0 };
}
if (
text.includes('WITH observation AS') &&
!text.includes('FROM "ql3"."trigger_schedules"')
) {
return {
rows: overrides.recoveryRows ?? [
{
observedAtMs: '1',
kind: null,
id: null,
runId: null,
status: null,
createdAtMs: null,
},
],
};
}
if (
overrides.runtimeScans &&
text.includes('FROM "ql3"."trigger_schedules"')
) {
events.push('scan:schedules');
return { rows: [], rowCount: 0 };
}
if (
overrides.runtimeScans &&
text.includes(
'FROM "ql3"."plugin_package_workflow_admissions" AS admission',
)
) {
events.push('scan:workflow-frontier');
return { rows: [], rowCount: 0 };
}
throw new Error(`unexpected query: ${text}`);
},
async connect() {
if (overrides.runtimeScans) {
return {
async query(text) {
if (
text === 'BEGIN ISOLATION LEVEL READ COMMITTED' ||
text.startsWith('SET LOCAL ') ||
text === 'COMMIT' ||
text === 'ROLLBACK'
) {
return { rows: [], rowCount: 0 };
}
if (
text.includes('attempt.lease_expires_at_ms') &&
text.includes('LEFT JOIN candidates AS candidate')
) {
events.push('scan:runtime-recovery');
return {
rows: [
{
observedAtMs: '1',
attemptId: null,
runId: null,
status: null,
createdAtMs: null,
},
],
rowCount: 1,
};
}
if (
text.includes(
'"ql3"."plugin_package_workflow_task_attempt_admissions"',
)
) {
events.push('scan:workflow-task-attempts');
return { rows: [], rowCount: 0 };
}
throw new Error(`unexpected repository query: ${text}`);
},
release() {
events.push('release:workflow-task-attempts');
},
};
}
throw new Error('Repository connections are not used during bootstrap');
},
};
return {
pool,
async close() {
events.push('close-database');
if (overrides.closeError) throw overrides.closeError;
},
};
}
function activationStack(
events,
recovery = { safe: true, remaining: 0, failed: 0 },
) {
return {
async reconcile() {
events.push('reconcile');
return recovery;
},
async startLifecycles() {
events.push('start-lifecycles');
return true;
},
installAdmission() {
events.push('install-admission');
return () => events.push('dispose-admission');
},
async stop() {
events.push('stop-stack');
return 'stopped';
},
};
}
function bootstrapOptions(events, overrides = {}) {
return {
enabled: true,
profile: 'cluster-control',
apiCredentialPepper: 'A'.repeat(43),
recovery: { ownerId: 'test-replica', providers: [] },
async openDatabase() {
events.push('open-database');
return databaseResource(events);
},
create(input) {
const {
evidence,
authenticator,
policies,
runs,
runCancellation,
taskDefinitions,
taskExecutionRevisions,
triggers,
schedules,
trustedToolStorage,
securityAudit,
} = input;
events.push('create-stack');
assert.equal('pool' in input, false);
assert.equal('recovery' in input, false);
assert.equal('recoveryClaims' in input, false);
assert.equal('recoveryTransitions' in input, false);
assert.equal(
evidence.contractVersion,
postgresqlControlSchemaContract.contractVersion,
);
assert.equal(typeof authenticator.authenticate, 'function');
assert.equal(typeof policies.resolve, 'function');
assert.equal(typeof runs.transaction, 'function');
assert.equal(typeof runCancellation.requestUserCancellation, 'function');
assert.equal(
typeof taskDefinitions.findCurrentTaskDefinition,
'function',
);
assert.equal('appendTaskDefinitionRevision' in taskDefinitions, false);
assert.equal(
typeof taskExecutionRevisions.resolveClusterTaskExecutionRevision,
'function',
);
assert.equal(typeof triggers.findCurrentTrigger, 'function');
assert.equal('appendTriggerRevision' in triggers, false);
assert.equal(typeof schedules.claimNextClusterSchedule, 'function');
assert.equal(typeof schedules.commitClusterScheduleDecision, 'function');
assert.equal(Object.isFrozen(trustedToolStorage), true);
assert.equal(
typeof trustedToolStorage.invocationArtifacts.findInput,
'function',
);
assert.equal(typeof trustedToolStorage.stepRuns.findById, 'function');
assert.equal(
typeof trustedToolStorage.startBarriers.findByStartId,
'function',
);
assert.equal(
typeof trustedToolStorage.completions.findByStartId,
'function',
);
assert.equal(
typeof trustedToolStorage.failureCompletions.findByStartId,
'function',
);
assert.equal(
typeof trustedToolStorage.resultKeyCatalog.findCurrent,
'function',
);
assert.equal(trustedToolStorage.resultKeyCatalog.append, undefined);
assert.equal(
typeof trustedToolStorage.resultRekeys.findHeadByArtifactId,
'function',
);
assert.equal(trustedToolStorage.resultRekeys.append, undefined);
assert.equal(
typeof trustedToolStorage.toolDefinitionSnapshots.findCurrent,
'function',
);
assert.equal(typeof securityAudit.record, 'function');
return activationStack(events);
},
audit(record) {
events.push(`audit:${record.state}`);
},
...overrides,
};
}
test('disabled and wrong-profile bootstrap never opens PostgreSQL', async () => {
const disabledEvents = [];
const disabled = await bootstrapClusterControlRuntime(
bootstrapOptions(disabledEvents, { enabled: false }),
);
assert.equal(disabled.status, 'disabled');
assert.deepEqual(disabledEvents, ['audit:disabled']);
const wrongProfileEvents = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(wrongProfileEvents, { profile: 'edge' }),
),
/cannot activate cluster-control/,
);
assert.deepEqual(wrongProfileEvents, []);
});
test('rejects a missing credential pepper before opening PostgreSQL', async () => {
const events = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(events, { apiCredentialPepper: undefined }),
),
/API credential configuration is invalid/,
);
assert.deepEqual(events, []);
});
test('rejects missing or unbounded recovery configuration before opening PostgreSQL', async () => {
const missingEvents = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(missingEvents, { recovery: undefined }),
),
/requires bounded recovery configuration/,
);
assert.deepEqual(missingEvents, []);
const timeoutEvents = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(timeoutEvents, {
recovery: {
ownerId: 'test-replica',
claimLeaseMs: 1_000,
providerTimeoutMs: 900,
},
}),
),
/250ms for fenced settlement/,
);
assert.deepEqual(timeoutEvents, []);
const localClockEvents = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(localClockEvents, {
scheduler: { clock: () => 1 },
}),
),
/scheduler configuration is invalid/,
);
assert.deepEqual(localClockEvents, []);
});
test('readiness failure closes the database before returning the root error', async () => {
const events = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(events, {
async openDatabase() {
events.push('open-database');
return databaseResource(events, { serverVersionNum: '150018' });
},
}),
),
(error) =>
error instanceof PostgresSchemaReadinessError &&
error.code === 'server_version_unsupported',
);
assert.equal(events.includes('create-stack'), false);
assert.deepEqual(events.slice(-2), ['audit:failed', 'close-database']);
});
test('opens once, assembles after readiness, and closes after stack shutdown', async () => {
const events = [];
const result = await bootstrapClusterControlRuntime(bootstrapOptions(events));
assert.equal(result.status, 'active');
assert.equal(events.filter((event) => event === 'open-database').length, 1);
assert.ok(events.indexOf('create-stack') > events.lastIndexOf('query:8'));
const first = result.stop();
assert.equal(first, result.stop());
assert.equal(await first, 'stopped');
assert.deepEqual(events.slice(-4), [
'dispose-admission',
'stop-stack',
'audit:stopped',
'close-database',
]);
});
test('injects reviewed Worker operations without exposing the runtime Pool', async () => {
const events = [];
const artifactStore = {
async put() {
throw new Error('not invoked during assembly');
},
async inspect() {
throw new Error('not invoked during assembly');
},
};
const result = await bootstrapClusterControlRuntime(
bootstrapOptions(events, {
workerRuntime: { artifactStore },
create(input) {
events.push('create-stack');
assert.equal('pool' in input, false);
assert.equal(Object.isFrozen(input.workerRuntime), true);
assert.equal(typeof input.workerRuntime.offers.claimNext, 'function');
assert.equal(
typeof input.workerRuntime.activation.acknowledgeStarting,
'function',
);
assert.equal(input.workerRuntime.secrets, undefined);
assert.equal(typeof input.workerRuntime.artifacts.upload, 'function');
assert.equal(
typeof input.workerRuntime.completion.complete,
'function',
);
assert.equal(
typeof input.workerRuntime.leaseControl.control,
'function',
);
return activationStack(events);
},
}),
);
assert.equal(result.status, 'active');
assert.equal(await result.stop(), 'stopped');
});
test('production scheduler cadence scans schedules, Workflow frontier, and Task Attempt admission', async () => {
const events = [];
let resolveCycle;
const cycle = new Promise((resolve) => {
resolveCycle = resolve;
});
const result = await bootstrapClusterControlRuntime(
bootstrapOptions(events, {
scheduler: {
intervalMs: 250,
onDiagnostic(error, summary) {
resolveCycle({ error, summary });
},
},
cancellationConvergence: { intervalMs: 60 * 60_000 },
async openDatabase() {
events.push('open-database');
return databaseResource(events, { runtimeScans: true });
},
}),
);
const observed = await Promise.race([
cycle,
new Promise((_, reject) => {
const timeout = setTimeout(
() => reject(new Error('production scheduler cycle did not run')),
2_000,
);
timeout.unref?.();
}),
]);
assert.equal(observed.error, undefined);
assert.deepEqual(observed.summary, {
firstClaimAcquiredAtMs: null,
lastClaimAcquiredAtMs: null,
claimed: 0,
initialized: 0,
skipped: 0,
admitted: 0,
raced: 0,
saturated: false,
});
assert.deepEqual(
events.filter((event) => event.startsWith('scan:')),
[
'scan:runtime-recovery',
'scan:lost-retry',
'scan:schedules',
'scan:workflow-frontier',
'scan:workflow-task-attempts',
],
);
assert.equal(await result.stop(), 'stopped');
assert.ok(events.includes('release:workflow-task-attempts'));
});
test('unsafe recovery stops the stack and closes the database', async () => {
const events = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(events, {
create({ runs }) {
events.push('create-stack');
assert.equal(typeof runs.findRunById, 'function');
return activationStack(events, {
safe: false,
remaining: 1,
failed: 0,
});
},
}),
),
/did not converge safely/,
);
assert.equal(events.includes('install-admission'), false);
assert.deepEqual(events.slice(-3), [
'stop-stack',
'audit:failed',
'close-database',
]);
});
test('bootstrap-owned recovery blocks a false-safe stack when the claim store is unavailable', async () => {
const events = [];
await assert.rejects(
bootstrapClusterControlRuntime(
bootstrapOptions(events, {
async openDatabase() {
events.push('open-database');
return databaseResource(events, {
recoveryRows: [
{
observedAtMs: '1',
kind: 'run',
id: 'run-1',
runId: 'run-1',
status: 'running',
createdAtMs: '1',
},
{
observedAtMs: '1',
kind: 'attempt',
id: 'attempt-1',
runId: 'run-1',
status: 'running',
createdAtMs: '2',
},
],
});
},
}),
),
(error) =>
error?.name === 'ClusterControlRecoveryStoreError' &&
error.retryable === true,
);
assert.equal(events.includes('install-admission'), false);
assert.equal(events.includes('reconcile'), false);
assert.deepEqual(events.slice(-3), [
'stop-stack',
'audit:failed',
'close-database',
]);
});
test('database close failure does not skip stack shutdown and remains idempotent', async () => {
const events = [];
const closeError = new Error('database close failed');
const result = await bootstrapClusterControlRuntime(
bootstrapOptions(events, {
async openDatabase() {
events.push('open-database');
return databaseResource(events, { closeError });
},
}),
);
const first = result.stop();
assert.equal(first, result.stop());
await assert.rejects(first, (error) => error === closeError);
assert.deepEqual(events.slice(-4), [
'dispose-admission',
'stop-stack',
'audit:stopped',
'close-database',
]);
});
@@ -0,0 +1,201 @@
const assert = require('node:assert/strict');
const path = require('node:path');
const { test } = require('node:test');
const {
loadPostgresCertificateAuthorityFile,
} = require('@qinglong/cluster-postgres/runtime');
const {
ClusterControlConfigError,
createClusterControlDatabaseBinding,
loadClusterControlConfig,
} = require('@qinglong/cluster-control/config');
const CA_FILE = path.join(__dirname, 'fixtures', 'mtls', 'ca-cert.pem');
const CA_BUNDLE = loadPostgresCertificateAuthorityFile(CA_FILE);
const BASE_ENV = Object.freeze({
QL3_CLUSTER_CONTROL_ENABLED: 'true',
QL_DEPLOYMENT_PROFILE: 'cluster-control',
QL3_POSTGRES_RUNTIME_URL:
'postgresql://ql3_runtime:secret@database.internal:5432/qinglong',
QL3_POSTGRES_TLS_SERVERNAME: 'database.internal',
QL3_API_CREDENTIAL_PEPPER: 'A'.repeat(43),
});
test('disabled configuration does not read PostgreSQL credentials', () => {
const reads = [];
const environment = new Proxy(
{
QL3_CLUSTER_CONTROL_ENABLED: 'false',
QL_DEPLOYMENT_PROFILE: 'standalone',
},
{
get(target, property, receiver) {
reads.push(property);
if (
property === 'QL3_POSTGRES_RUNTIME_URL' ||
property === 'QL3_API_CREDENTIAL_PEPPER'
) {
throw new Error('credential must not be read');
}
return Reflect.get(target, property, receiver);
},
},
);
assert.deepEqual(loadClusterControlConfig(environment), {
enabled: false,
profile: 'standalone',
});
assert.equal(reads.includes('QL3_POSTGRES_RUNTIME_URL'), false);
assert.equal(reads.includes('QL3_API_CREDENTIAL_PEPPER'), false);
});
test('enabled configuration requires the exact cluster-control profile', () => {
assert.throws(
() =>
loadClusterControlConfig({
...BASE_ENV,
QL_DEPLOYMENT_PROFILE: 'standalone',
}),
ClusterControlConfigError,
);
assert.throws(
() =>
loadClusterControlConfig({
...BASE_ENV,
QL3_CLUSTER_CONTROL_ENABLED: 'yes',
}),
ClusterControlConfigError,
);
});
test('builds an exact runtime-only TLS-verified Pool configuration', async () => {
const config = loadClusterControlConfig({
...BASE_ENV,
QL3_CLUSTER_HTTP_HOST: '127.0.0.1',
QL3_CLUSTER_HTTP_PORT: '5900',
QL3_CLUSTER_HTTP_MAX_IN_FLIGHT: '32',
QL3_CLUSTER_AUTH_RATE_WINDOW_MS: '30000',
QL3_CLUSTER_AUTH_RATE_PER_PEER: '20',
QL3_CLUSTER_AUTH_RATE_GLOBAL: '200',
QL3_CLUSTER_AUTH_RATE_MAX_PEERS: '512',
QL3_POSTGRES_MAX_CONNECTIONS: '12',
QL3_POSTGRES_TLS_SERVERNAME: 'database.internal',
QL3_POSTGRES_TLS_CA_FILE: CA_FILE,
});
assert.equal(config.enabled, true);
assert.deepEqual(config.http, {
host: '127.0.0.1',
port: 5900,
maxBodyBytes: 1024 * 1024,
maxInFlightRequests: 32,
authenticationRateWindowMs: 30_000,
authenticationRatePerPeer: 20,
authenticationRateGlobal: 200,
authenticationRateMaxPeers: 512,
requestTimeoutMs: 15_000,
drainTimeoutMs: 10_000,
});
assert.deepEqual(config.database, {
connection: {
connectionString: BASE_ENV.QL3_POSTGRES_RUNTIME_URL,
tls: {
mode: 'verify-full',
ca: CA_BUNDLE,
servername: 'database.internal',
},
},
pool: {
applicationName: 'qinglong-cluster-runtime',
maxConnections: 12,
connectionTimeoutMs: 5_000,
},
});
assert.deepEqual(config.security, {
apiCredentialPepper: BASE_ENV.QL3_API_CREDENTIAL_PEPPER,
});
const binding = createClusterControlDatabaseBinding(config);
assert.equal(binding.availability.status, 'available');
const database = await binding.openDatabase();
await database.close();
});
test('loads discrete operator-managed runtime credentials without a DSN copy', () => {
const {
QL3_POSTGRES_RUNTIME_URL: _connectionString,
...withoutConnectionString
} = BASE_ENV;
const config = loadClusterControlConfig({
...withoutConnectionString,
QL3_POSTGRES_RUNTIME_HOST: 'ql3-postgres-rw.qinglong3-system.svc',
QL3_POSTGRES_RUNTIME_PORT: '5432',
QL3_POSTGRES_RUNTIME_DATABASE: 'qinglong',
QL3_POSTGRES_RUNTIME_USER: 'ql3_runtime',
QL3_POSTGRES_RUNTIME_PASSWORD: 'operator-secret',
});
assert.deepEqual(config.database.connection, {
host: 'ql3-postgres-rw.qinglong3-system.svc',
port: 5432,
database: 'qinglong',
user: 'ql3_runtime',
password: 'operator-secret',
tls: {
mode: 'verify-full',
servername: 'database.internal',
},
});
});
test('requires a second explicit gate before disabling PostgreSQL TLS', () => {
assert.throws(
() =>
loadClusterControlConfig({
...BASE_ENV,
QL3_POSTGRES_TLS_MODE: 'disable',
}),
/requires QL3_POSTGRES_ALLOW_INSECURE=true/,
);
const config = loadClusterControlConfig({
...BASE_ENV,
QL3_POSTGRES_TLS_MODE: 'disable',
QL3_POSTGRES_ALLOW_INSECURE: 'true',
});
assert.deepEqual(config.database.connection.tls, { mode: 'disable' });
});
test('rejects TLS query overrides, missing credentials and unbounded values', () => {
for (const environment of [
{
...BASE_ENV,
QL3_POSTGRES_RUNTIME_URL:
'postgresql://database.internal/qinglong?sslmode=disable',
},
{ ...BASE_ENV, QL3_POSTGRES_TLS_SERVERNAME: undefined },
{ ...BASE_ENV, QL3_POSTGRES_TLS_SERVERNAME: '127.0.0.1' },
{ ...BASE_ENV, QL3_POSTGRES_TLS_CA_FILE: 'relative-ca.pem' },
{
...BASE_ENV,
QL3_POSTGRES_TLS_MODE: 'disable',
QL3_POSTGRES_ALLOW_INSECURE: 'true',
QL3_POSTGRES_TLS_CA_FILE: CA_FILE,
},
{ ...BASE_ENV, QL3_POSTGRES_RUNTIME_URL: '' },
{
...BASE_ENV,
QL3_POSTGRES_RUNTIME_HOST: 'database.internal',
},
{ ...BASE_ENV, QL3_POSTGRES_MAX_CONNECTIONS: '65' },
{ ...BASE_ENV, QL3_CLUSTER_HTTP_MAX_BODY_BYTES: '99999999' },
{ ...BASE_ENV, QL3_CLUSTER_AUTH_RATE_WINDOW_MS: '99999999' },
{ ...BASE_ENV, QL3_CLUSTER_AUTH_RATE_PER_PEER: '0' },
{ ...BASE_ENV, QL3_CLUSTER_AUTH_RATE_GLOBAL: '1000001' },
{ ...BASE_ENV, QL3_CLUSTER_AUTH_RATE_MAX_PEERS: '65537' },
{ ...BASE_ENV, QL3_API_CREDENTIAL_PEPPER: 'weak' },
]) {
assert.throws(
() => loadClusterControlConfig(environment),
ClusterControlConfigError,
);
}
});
@@ -0,0 +1,50 @@
const assert = require('node:assert/strict');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { test } = require('node:test');
test('public application, config, admission and route exports exclude migration and legacy modules', () => {
const packageDirectory = path.resolve(__dirname, '..');
const script = `
const application = require('@qinglong/cluster-control/application');
const config = require('@qinglong/cluster-control/config');
const admission = require('@qinglong/cluster-control/admission');
const routes = require('@qinglong/cluster-control/routes');
const runRoutes = require('@qinglong/cluster-control/run-routes');
const apiCredential = require('@qinglong/cluster-control/api-credential');
const loaded = Object.keys(require.cache).map((file) => file.replaceAll('\\\\', '/'));
process.stdout.write(JSON.stringify({
hasStart: typeof application.startClusterControlApplication === 'function',
hasConfig: typeof config.loadClusterControlConfig === 'function',
hasAdmission: typeof admission.createClusterControlAdmissionPipeline === 'function',
hasRoutes: typeof routes.createClusterControlRouteRegistry === 'function',
hasRunReadRoute: typeof runRoutes.createClusterControlRunReadRoute === 'function',
hasApiCredential: typeof apiCredential.createClusterControlApiCredentialAuthenticator === 'function',
loaded,
}));
`;
const result = spawnSync(process.execPath, ['-e', script], {
cwd: packageDirectory,
encoding: 'utf8',
});
assert.equal(result.status, 0, result.stderr);
const report = JSON.parse(result.stdout);
assert.equal(report.hasStart, true);
assert.equal(report.hasConfig, true);
assert.equal(report.hasAdmission, true);
assert.equal(report.hasRoutes, true);
assert.equal(report.hasRunReadRoute, true);
assert.equal(report.hasApiCredential, true);
assert.equal(
report.loaded.some(
(file) =>
file.includes('/back/') ||
/\/ql3-cluster-postgres\/dist\/migrations\/pg-\d/.test(file) ||
file.endsWith('/ql3-cluster-postgres/dist/migration/migrate.js') ||
file.endsWith('/ql3-cluster-postgres/dist/migration/migration.js') ||
file.endsWith('/ql3-cluster-postgres/dist/schema/schema.js'),
),
false,
report.loaded.join('\n'),
);
});
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDMTCCAhmgAwIBAgIUT09qZ5rsuZyfzb0ngF3gc/MLVT0wDQYJKoZIhvcNAQEL
BQAwIDEeMBwGA1UEAwwVUWluZ0xvbmctMy1UZXN0LUNBLXYyMB4XDTI2MDcxOTE1
MDU0M1oXDTQ2MDcxNDE1MDU0M1owIDEeMBwGA1UEAwwVUWluZ0xvbmctMy1UZXN0
LUNBLXYyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3ZjZrBMqBu5U
fGgnp4j9ohhwq2iCW9Y1U8tkFvxpca9SJawQAZE8U0D0fy1JPUOrUHP1j5PzDMk3
CC3WfrP/e26Xx/BwXp45QfZS24vHPu8+0KCo3FfNoQPcXra58+XpNh6AhLdk1cMl
/kSFDC/fwo4elETiETdmCD7g/JwvM0U4l+5yGN4lW0Wb7P4S+6Red1xxt9QWT6b7
FREoXtL/Ego69T8kabLH38WlUVm+Mfhb18yMTlDDpOI83xbxDUinfivGREr41QUe
RGE2+I1XKjztXqowdkIb3T3JVDx/WMI9+4m2vOX1ZJNjS0ZpaEeMzlNLSJKNQaMR
uOmDqBPJJwIDAQABo2MwYTAdBgNVHQ4EFgQUZw224kl/JxHPu4khthe4bwJw5JIw
HwYDVR0jBBgwFoAUZw224kl/JxHPu4khthe4bwJw5JIwDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAF6yWGbSYV7PT5+G
6Am4M7n2xpy6EjtKN6jaeh46K6xTReooTJKj3Yix/AYYYG/Bh24CCjg7zxIq7CzL
gPoJjGiSJVZBguHwSmzo1HajJJ7ncumT5cZwXbxHQ521CqQbskrltPJNf3P5tKq5
AzCJ2KrTmi30LNbH1G1v9bkwsVv+T4aWYN+o3cIpMPklLvWKq381dT7eN0/O/rwK
wUUXOUYFClHqm7M5jOQNrt7dneHU/R9GEYGWCw/T9FwNe7QovhcI22Q5xtbJZ9HR
B/8/OZ+yB6ZmkPnw1STJ1YOckorSRNQLFOchdZP7CeXFOTRmU9KHoIka+zG9aOab
/NVGuLU=
-----END CERTIFICATE-----
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDKzCCAhOgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVUWlu
Z0xvbmctMy1UZXN0LUNBLXYyMB4XDTI2MDcxOTE1MDYzOFoXDTQ2MDcxNDE1MDYz
OFowGjEYMBYGA1UEAwwPcWwzLXRlc3Qtd29ya2VyMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEArmm2syXGg03ZHQgoMknVv+QC9qVGA46Xnr8K/lgppZID
H/7obm8P0oiAbgOQbKhEzoY+9fjoW2VWBKft1PkVfzjxBvsU/76KIrluKBn+wwtr
LEkPxHaawYTtMUm6Urvxq2VlzU7pdZVJ1PLWovBxyhwe8OUagkvtqYkLN3t6YHZo
kMgIjHKx8SKWUZRQ846V6mm2/8e+lO+BTd/wb7cEvZDYbqoKaqlm91sO6NDuvAJl
DrLLQprdBg4kn6rI1/T2vztuz7TkAgAlkJyDJ2rz+VGSTZ6NxFKaV4hbF8b5RA4S
SKDwqaNtFYoS+p5gJsyNSGvMOKUzMCiyapd8fB6tBQIDAQABo3UwczAMBgNVHRMB
Af8EAjAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjAdBgNV
HQ4EFgQUj2XZPOHX/LbNTC2Uolt6HAkofbUwHwYDVR0jBBgwFoAUZw224kl/JxHP
u4khthe4bwJw5JIwDQYJKoZIhvcNAQELBQADggEBAGsbsLjPTnev7JCpfC88HmgW
pL22q93WDQ407O2+t/5dOc5AyZZx9kR6cUKfKT/JlseDN6x8V0yVDeq6OtKKYmDg
xB8P1n/e/GCjx3w3cYWhP5bNw9yNNTIReG3DnKGr4Q6Xe8YA415o7GQSp24oNa0R
Kx3xCr+y3VLOu6eeotsSb7tezcEvGTFXAd61wNMPIjyN1ZevIoNcMnYUbjAkH2py
sDVP/8XjQCcU9vXTZPVtxGKkC+w6oiYpG96kixe//N8IgruiJHL/74/RxiOR0Zt/
MDnrYFyS3Mp9hMHGbhJ41NhaUUDr7IAQQqsY9su6CMS50DtgSwZQ+kkzf+NBo4I=
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCuabazJcaDTdkd
CCgySdW/5AL2pUYDjpeevwr+WCmlkgMf/uhubw/SiIBuA5BsqETOhj71+OhbZVYE
p+3U+RV/OPEG+xT/vooiuW4oGf7DC2ssSQ/EdprBhO0xSbpSu/GrZWXNTul1lUnU
8tai8HHKHB7w5RqCS+2piQs3e3pgdmiQyAiMcrHxIpZRlFDzjpXqabb/x76U74FN
3/BvtwS9kNhuqgpqqWb3Ww7o0O68AmUOsstCmt0GDiSfqsjX9Pa/O27PtOQCACWQ
nIMnavP5UZJNno3EUppXiFsXxvlEDhJIoPCpo20VihL6nmAmzI1Ia8w4pTMwKLJq
l3x8Hq0FAgMBAAECggEADWf/zJ5rN12NAj/FwzPMs87FcYr7qjtUKTYbFLjXHqxu
BBAWcqKjzzWB1bL9b3SxsUQ9Y6/JwHi2F26HB/dLcTeBw10aXJwbRxcEwh1ZaUmf
58wH47yQIa7jgoXdxg0/QsLtA858gHbEsZMm72hW/seRk0ew9XH7pyvrCk05918S
IvtN49gZdffw0O/V89Mys1ZA/w/hsi6NuZI0zEIjOMWN1hMPKYHbVbaYaIX0mC6P
Yg26UXqf8g6SgW/dYLt28r69yGvKBwyztYw47Ieo2NyWToZsHHg5ypdjUXnt++wn
sFPNo28KVu/NYTGY6Y2TphamdZTUetQ+0AfeITTiYwKBgQDaa6gmDHqHEPwdgLmu
fwrKNiU/NwNx8ZwEMYYvlyCmmXRLbTFux3AE1xHUUQac1PAhmelZwulm3tInQ94F
pDTkjfcZt/K83gLkNVLbNepbVKfrvcAHbPcoBC3k161RcrYW0LxR/P8z/YXyzkUI
04/g90INoKKITc8hoI1Ttmyn3wKBgQDMa72ICOa/HFmoT/G8xWkqU71F1rKGanJ3
sTPZXjGEyx0atSWB0XfmBd0hiIAx1DWuUo4EZ9Yul1B97gmnJSxbXGcKTfp8+1kS
YySIx1GE0qG23n/xppeT0xPUCyJl/UuAKEKi7l3ZtT4RiaDP/KRo2LuKmCXuxSSa
6d5YYsAXmwKBgQC8fEjXxM77vZmDMDGMNs+t3nnYCnZrns8/AynD6cvgWO51pJ4S
9gJh+uLE8MMfFda/eF1Z+4yFHGYIQlXXUnPeg/An/oh3mbKvEqEU0HsUI4LhOZb8
EXlby/d60vAbKD2ghLLuob/tMquj84K5cjBoN5eFwQhhTZwgbbdn9TXgywKBgHxG
6BQi7T20m6Fi8OFF1xi/jis+SozDfFHeLlpxFIPaBBivllzlHxJ98CEp3l3s2LHh
SKvPAPyUS4AzzjSKufvVImO2YpnHKTCvi4+INbwdXelSPdCI6lAZnE1mc4QzyMni
MBjj97SapmB9HoIz4zRGx0WMGEugGRABLIbpikUPAoGAbdUAdEwQIIcOZEvl5spC
CNX2AMXCIQk4qkauJo+c/fQJXL/nHaO3reOXSJc1QjB1ceoZ+s0WuN7avkwy+jJ/
ZXGxdo/ewFNYTqELnnN2/Kr6GDvpwrlwGhdNpp2Sxjnyq7DAxnfbaTdhUo+WGV70
+VIzVYYa+dhmUFNEvWtwt4Y=
-----END PRIVATE KEY-----
@@ -0,0 +1,10 @@
-----BEGIN X509 CRL-----
MIIBeTBjAgEBMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNVBAMMFVFpbmdMb25nLTMt
VGVzdC1DQS12MhcNMjYwNzE5MTUwNjQ0WhcNNDYwNzE0MTUwNjQ0WqAPMA0wCwYD
VR0UBAQCAhAAMA0GCSqGSIb3DQEBCwUAA4IBAQA3HS0UP59J+d0bRTTjFzLRHaaX
BUr/2FBK7YcKr6wiSa8EVhiiRZ6zcPShIfKPDhNn/rozMJST0SyO4aH6A+BOuAsl
jK37Cz1gdDcdnNTo87/QTPVSe1wQqNHu8fJSQgQMIOqjUigB8crIhcNBJO7wSVxl
AELA5XV6QiVKT3vQZjfY6o99y1790x+BjcIi/zWSFRY5dFFLVCuPgxj+fR6+S04V
cAVAWNt9z+eRf0uYbV+VseT2Xb5HieR/PZCN2oc3xyEQsNs5m2txCAy+ld3sUl/z
2FDP+JBla05W+0xP8Ql5sCd1NZahrrxVvbRFJcJG25sRuYOGZVNHbtqMGD5U
-----END X509 CRL-----
@@ -0,0 +1,11 @@
-----BEGIN X509 CRL-----
MIIBkDB6AgEBMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNVBAMMFVFpbmdMb25nLTMt
VGVzdC1DQS12MhcNMjYwNzE5MTUwNjUzWhcNNDYwNzE0MTUwNjUzWjAVMBMCAhAB
Fw0yNjA3MTkxNTA2NDlaoA8wDTALBgNVHRQEBAICEAEwDQYJKoZIhvcNAQELBQAD
ggEBALG0R1hPya3WzAeLkGqcGoVz3HLDn1igESMSK34532NWSJwq161sZXLogy+K
J1+NL7g8C9FbMqP9ZkA36rKt4TDRpdoLO1uLKCUVEQ3O87iUVixyKTWU2lUDOk5P
ivJNf6y/TypKK5dOxkrOUH6eB4qmGPLZu1w8JWSbYlYLXueQ6NXR1mXeN4ZPaLi5
5HGxMdAjRRWkiRm4LFbeJEiIcDEAxOwfKObdH5eC+VSl9acECQfLOkJ38ku18gtH
Wha1Na5cQ6G6TwMbtQ60RXkdLmLonD3b/mRBoVwuIMbXq7tXOigc08+JANUjnair
Df9nrEWjhB8V4NoDsAYZddG/cEc=
-----END X509 CRL-----
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDQzCCAiugAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVUWlu
Z0xvbmctMy1UZXN0LUNBLXYyMB4XDTI2MDcxOTE1MDYyMloXDTQ2MDcxNDE1MDYy
MlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAwzxMyomJ5BxMMlRpZgmUd1t5SnEYxN7YLjOW5fdaI0tyg6r5R7Rm
Ykui0ArxInaICXecgXk9O7MydAT8+W+aU8VLOaBA/lY6waFMrMGinCrmIXjuaB/H
POeslX4hvx6i1teOGA1HmF0ELyDbuUNlIQjJ12sKJ3+FLo9vZf+JjRW3DP0Yt1tB
mxWURb51VSnLzBTCp/INlm25/DZ7M+Kr2gqXtHoegO6aqTRWIRgCXlVKpCoaA2lv
1RY1LwPY1KhTM/fUMYz9/pwwDPmKFRQbvH6lBRl3tayP0uR1jIheOAeC5/F2A1TA
G5LMusSQ0vrn1+wolkB4qf6bBgTK5y6R4wIDAQABo4GSMIGPMAwGA1UdEwEB/wQC
MAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMBoGA1UdEQQT
MBGCCWxvY2FsaG9zdIcEfwAAATAdBgNVHQ4EFgQUDB+Pt69+4JOSkxeky0fLzgVN
9UIwHwYDVR0jBBgwFoAUZw224kl/JxHPu4khthe4bwJw5JIwDQYJKoZIhvcNAQEL
BQADggEBAJpLx9FSXd426hE8q7QbhAyaK27/li7J/X93Zzkf7FV7mHFpl22SZ5No
airtCM41hZXBY9BTb0OGzHOb4L7Yef5c6zjKrsDNlWixAunJg7kMJ+So8DhLugXo
OIjWV1qX3dkx7oPg9I9s62rZGdQPFhmz+YAz/7WYAeNBpnwC0bCBH2ksOo0SArEA
W4S0SlX+3g+vTk9f1JM90ytqDKU7fCcHFCuI+kM5IcWIjhvejpxmmywFYBXvng2+
dL2xOmhyTWey+Q057JFqG8UNYF75OJ/Q090Lq/QVCMX7715wG8Uwe6InMll/otgl
u6GSeoX8wp5aM6+IBynaRhdpJoSK/Ik=
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDPEzKiYnkHEwy
VGlmCZR3W3lKcRjE3tguM5bl91ojS3KDqvlHtGZiS6LQCvEidogJd5yBeT07szJ0
BPz5b5pTxUs5oED+VjrBoUyswaKcKuYheO5oH8c856yVfiG/HqLW144YDUeYXQQv
INu5Q2UhCMnXawonf4Uuj29l/4mNFbcM/Ri3W0GbFZRFvnVVKcvMFMKn8g2Wbbn8
Nnsz4qvaCpe0eh6A7pqpNFYhGAJeVUqkKhoDaW/VFjUvA9jUqFMz99QxjP3+nDAM
+YoVFBu8fqUFGXe1rI/S5HWMiF44B4Ln8XYDVMAbksy6xJDS+ufX7CiWQHip/psG
BMrnLpHjAgMBAAECggEAK7fvjDUjWyDZETYkyfQgOmAR95D+1mr+Zs4nnsR2e0vU
T1a0bpGfkahVQ5gHqkt3qAMQWU/gXeOBS0ioq2SJ3vNhpnxTwYBmtOBdTpwnwSe8
E8fEPy3KjEFmvK0benQuS8TGLW2f2CcQHkuR2FkWpsCUjjgKdJDSLnxxGhqQXzaW
3sVFH6m6tYtOqcGPJscNkKiPh2fHIfKgiKLCVZSM4zw1FRXFk9c9dfnu1yNKeKZE
fDn7p5+bDHQJio/w0AbOO90satqHYJEirPEuhrTQ6xo3jkJ+u0XWZ0n5fkxyHaiU
oJcQ1BZ2VCMK2uD3MnlW1Kea8HeVWFAAaoNMjt278QKBgQDrVSlqRXdtSVaGu9WT
20jVttUrcZl1D8G5CEO+PfMmMtJ8HyanlZPIiOzbSIWw94EXW6qpMeq2nxIn6xJI
rJKWDkkYkAjScVfH6nNxyg4FDpbzMbD6ZN+KBkdbj7QyjYqo3PbSjFdDu5BDtKa1
J5xaIWc8jB2R57yOekm3+QvPswKBgQDUYajbr+owoN9G8ij9PbpnacnrK8l+MvZO
kwYlKB/IFXa+Zp9GA1W0ViyndTd8Jjq+JYXn4XpZyoPznLmxKUlXrlUBLgml2+nX
gUYKerW2h3k1xd7mWalG2Iwqykkm27qSjeJteQsjI2lb+d2F7J4+wufWOXzfT+FP
ISTYOmedEQKBgQC/lAyrHvIiXdm6xZ8RRyr8EGNpgolS6wjiAQBlFzmily0TLqjh
NLuiPRXVTizH7DWDNnSwWJqjIMw/AvdXgmWzEt4eyOLlrq+vaAWjVMIlC8OHJ6TT
stroGkRtHYesv91pqriknutLkJ3ZAG/WefCmzxqkB8zqwqSVuKfaxTwAUQKBgA99
f/9l+ULKuP5Hs02lLu9T+/I3I18dAHICrzQOlSLWze5dNci5fYrFrkQYfI+FiktM
GVQsUewypSUjjTeqtzq58GFBplp+i7O4Lg+dm5tQjjqpz/QlHf+Y08BbOLKo3BbM
WnN5nKJXdtvKgam+9FvHg3lQgBcZkGlhFXrf56bBAoGARWjH3qFhePIIR/o/YyU4
fPhLYtiIgIvr5Is4hkGcMIOu2bnmYUCHa/zBmLqKV4K3F4u4RWmieAiLmvwi+9YD
3xZ/RVIi5yK5CKCk7hOp4AVBF9bdLIxXiRSkIO6QMithomoYlc+UkfXHMGyZFQ6h
bxKAymile6IWwd4ALAsPb3U=
-----END PRIVATE KEY-----
@@ -0,0 +1,710 @@
const assert = require('node:assert/strict');
const { readFileSync } = require('node:fs');
const http = require('node:http');
const https = require('node:https');
const path = require('node:path');
const { test } = require('node:test');
const {
ClusterControlAdmissionDrainTimeoutError,
ClusterControlHttpConfigurationError,
startClusterControlHttpSurface,
} = require('@qinglong/cluster-control/http');
const EVIDENCE = Object.freeze({
contractName: 'control-core',
contractVersion: 2,
serverMajor: 16,
migrationIds: Object.freeze([
'pg-0001-schema-capability',
'pg-0002-run-core',
'pg-0003-run-retry-policy',
]),
});
const MTLS_FIXTURES = path.join(__dirname, 'fixtures', 'mtls');
const MTLS = Object.freeze({
privateKey: readFileSync(path.join(MTLS_FIXTURES, 'server-key.pem')),
certificateChain: readFileSync(path.join(MTLS_FIXTURES, 'server-cert.pem')),
clientCertificateAuthorities: Object.freeze([
readFileSync(path.join(MTLS_FIXTURES, 'ca-cert.pem')),
]),
});
function pipeline(handler) {
return {
async prepare(metadata) {
return {
handle(body) {
return handler({ ...metadata, body });
},
};
},
};
}
function request(address, options = {}) {
const rawBody = options.rawBody;
const body =
rawBody === undefined
? options.body === undefined
? undefined
: Buffer.from(JSON.stringify(options.body))
: Buffer.from(rawBody);
const headers = { connection: 'close', ...options.headers };
if (body && headers['content-length'] === undefined) {
headers['content-length'] = String(body.byteLength);
}
if (options.body !== undefined && headers['content-type'] === undefined) {
headers['content-type'] = 'application/json';
}
return new Promise((resolve, reject) => {
const outgoing = http.request(
{
host: address.host,
port: address.port,
method: options.method ?? 'GET',
path: options.path ?? '/',
headers,
},
(response) => {
const chunks = [];
response.on('data', (chunk) => chunks.push(chunk));
response.on('end', () => {
const text = Buffer.concat(chunks).toString('utf8');
resolve({
statusCode: response.statusCode,
headers: response.headers,
body: text.length === 0 ? null : JSON.parse(text),
});
});
},
);
outgoing.on('error', reject);
if (body) outgoing.write(body);
outgoing.end();
});
}
function secureRequest(address, withClientCertificate, options = {}) {
return new Promise((resolve, reject) => {
const outgoing = https.request(
{
host: '127.0.0.1',
servername: 'localhost',
port: address.port,
path: options.path ?? '/livez',
method: options.method ?? 'GET',
agent: options.agent,
ca: readFileSync(path.join(MTLS_FIXTURES, 'ca-cert.pem')),
...(withClientCertificate
? {
key: readFileSync(path.join(MTLS_FIXTURES, 'client-key.pem')),
cert: readFileSync(path.join(MTLS_FIXTURES, 'client-cert.pem')),
}
: {}),
minVersion: 'TLSv1.3',
maxVersion: 'TLSv1.3',
},
(response) => {
const tlsProtocol = response.socket.getProtocol();
const chunks = [];
response.on('data', (chunk) => chunks.push(chunk));
response.on('end', () => {
resolve({
statusCode: response.statusCode,
tlsProtocol,
headers: response.headers,
body: JSON.parse(Buffer.concat(chunks).toString('utf8')),
});
});
},
);
outgoing.once('error', reject);
outgoing.end();
});
}
test('requires a trusted client certificate on the TLS 1.3 surface', async (t) => {
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
mutualTls: MTLS,
});
t.after(() => surface.close());
await assert.rejects(secureRequest(surface.address, false));
const trusted = await secureRequest(surface.address, true);
assert.equal(trusted.statusCode, 200);
assert.equal(trusted.tlsProtocol, 'TLSv1.3');
assert.deepEqual(trusted.body, { status: 'live' });
});
test('reloads mTLS trust and CRLs without rebinding the listener', async (t) => {
const emptyCrl = readFileSync(path.join(MTLS_FIXTURES, 'empty-crl.pem'));
const revokedClientCrl = readFileSync(
path.join(MTLS_FIXTURES, 'revoked-client-crl.pem'),
);
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
mutualTls: {
...MTLS,
certificateRevocationLists: [emptyCrl],
},
});
t.after(() => surface.close());
const address = { ...surface.address };
assert.equal((await secureRequest(address, true)).statusCode, 200);
assert.equal(
surface.reloadMutualTls({
...MTLS,
certificateRevocationLists: [revokedClientCrl],
}),
2,
);
assert.deepEqual(surface.address, address);
await assert.rejects(secureRequest(address, true));
assert.equal(
surface.reloadMutualTls({
...MTLS,
certificateRevocationLists: [emptyCrl],
}),
3,
);
assert.equal((await secureRequest(address, true)).statusCode, 200);
assert.throws(
() =>
surface.reloadMutualTls({
...MTLS,
certificateChain: 'not a certificate',
certificateRevocationLists: [emptyCrl],
}),
ClusterControlHttpConfigurationError,
);
assert.equal((await secureRequest(address, true)).statusCode, 200);
});
test('forces pre-reload keep-alive sockets to reconnect before routing', async (t) => {
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
mutualTls: MTLS,
});
t.after(() => surface.close());
const agent = new https.Agent({ keepAlive: true, maxSockets: 1 });
t.after(() => agent.destroy());
let entered;
let release;
const enteredPromise = new Promise((resolve) => {
entered = resolve;
});
const releasePromise = new Promise((resolve) => {
release = resolve;
});
const dispose = surface.installAdmission(
EVIDENCE,
pipeline(async () => {
entered();
await releasePromise;
return { statusCode: 200, body: { status: 'completed' } };
}),
);
t.after(() => dispose());
const active = secureRequest(surface.address, true, {
agent,
path: '/api/v3/hold',
});
await enteredPromise;
assert.equal(surface.reloadMutualTls(MTLS), 2);
release();
assert.equal((await active).statusCode, 200);
const stale = await secureRequest(surface.address, true, { agent });
assert.equal(stale.statusCode, 503);
assert.deepEqual(stale.body, { code: 'tls_context_reloaded' });
assert.equal(stale.headers.connection, 'close');
assert.equal(
(await secureRequest(surface.address, true, { agent })).statusCode,
200,
);
});
test('exposes probes but rejects API work until admission is installed', async (t) => {
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
});
t.after(() => surface.close());
const live = await request(surface.address, { path: '/livez' });
assert.equal(live.statusCode, 200);
assert.deepEqual(live.body, { status: 'live' });
assert.equal(live.headers['cache-control'], 'no-store');
const invalidProbeMethod = await request(surface.address, {
method: 'POST',
path: '/livez',
});
assert.equal(invalidProbeMethod.statusCode, 405);
assert.deepEqual(invalidProbeMethod.body, { code: 'method_not_allowed' });
const waiting = await request(surface.address, { path: '/readyz' });
assert.equal(waiting.statusCode, 503);
assert.deepEqual(waiting.body, { status: 'not_ready' });
const rejected = await request(surface.address, {
method: 'POST',
path: '/api/v3/runs',
headers: { connection: 'keep-alive' },
body: { mustNotBeRead: true },
});
assert.equal(rejected.statusCode, 503);
assert.equal(rejected.headers.connection, 'close');
let observed;
const dispose = surface.installAdmission(
EVIDENCE,
pipeline(async (incoming) => {
observed = incoming;
return { statusCode: 201, body: { accepted: true } };
}),
);
const ready = await request(surface.address, { path: '/readyz' });
assert.equal(ready.statusCode, 200);
assert.deepEqual(ready.body, { status: 'ready' });
const admitted = await request(surface.address, {
method: 'POST',
path: '/api/v3/runs?tag=a&tag=b',
headers: { 'x-request-id': 'request-123' },
body: { taskId: 'task-1' },
});
assert.equal(admitted.statusCode, 201);
assert.deepEqual(admitted.body, { accepted: true });
assert.equal(admitted.headers['x-request-id'], 'request-123');
assert.equal(observed.requestId, 'request-123');
assert.equal(observed.method, 'POST');
assert.equal(observed.path, '/api/v3/runs');
assert.deepEqual(observed.query.tag, ['a', 'b']);
assert.deepEqual(observed.body, { taskId: 'task-1' });
await dispose();
assert.equal(
(await request(surface.address, { path: '/readyz' })).statusCode,
503,
);
assert.equal(
(
await request(surface.address, {
method: 'POST',
path: '/api/v3/runs',
body: { ignored: true },
})
).statusCode,
503,
);
});
test('enforces bounded JSON requests and responses without leaking failures', async (t) => {
const diagnostics = [];
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
maxBodyBytes: 1024,
maxResponseBytes: 1024,
onError(diagnostic) {
diagnostics.push(diagnostic);
},
});
t.after(() => surface.close());
const dispose = surface.installAdmission(
EVIDENCE,
pipeline(async (incoming) => {
if (incoming.path.endsWith('/large')) {
return { statusCode: 200, body: { value: 'x'.repeat(2048) } };
}
if (incoming.path.endsWith('/throw')) {
throw new Error('secret driver detail');
}
return { statusCode: 200, body: incoming.body };
}),
);
t.after(() => dispose());
const invalidJson = await request(surface.address, {
method: 'POST',
path: '/api/v3/test',
headers: { 'content-type': 'application/json' },
rawBody: '{',
});
assert.equal(invalidJson.statusCode, 400);
assert.deepEqual(invalidJson.body, { code: 'invalid_json' });
const unsupported = await request(surface.address, {
method: 'POST',
path: '/api/v3/test',
headers: { 'content-type': 'text/plain' },
rawBody: 'hello',
});
assert.equal(unsupported.statusCode, 415);
assert.deepEqual(unsupported.body, { code: 'unsupported_content_type' });
const oversized = await request(surface.address, {
method: 'POST',
path: '/api/v3/test',
headers: { 'content-type': 'application/json' },
rawBody: JSON.stringify({ value: 'x'.repeat(1024) }),
});
assert.equal(oversized.statusCode, 413);
assert.deepEqual(oversized.body, { code: 'request_too_large' });
const largeResponse = await request(surface.address, {
path: '/api/v3/large',
});
assert.equal(largeResponse.statusCode, 500);
assert.deepEqual(largeResponse.body, { code: 'response_too_large' });
const internal = await request(surface.address, {
path: '/api/v3/throw',
});
assert.equal(internal.statusCode, 500);
assert.deepEqual(internal.body, { code: 'internal_error' });
assert.equal(
JSON.stringify(internal).includes('secret driver detail'),
false,
);
assert.equal(
diagnostics.some(
(diagnostic) =>
diagnostic.phase === 'request' && diagnostic.path === '/api/v3/throw',
),
true,
);
});
test('completes admission preflight before reading an untrusted body', async (t) => {
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
});
t.after(() => surface.close());
const dispose = surface.installAdmission(EVIDENCE, {
async prepare() {
throw Object.assign(new Error('credential rejected'), {
statusCode: 401,
code: 'authentication_required',
});
},
});
t.after(() => dispose());
const rejected = await request(surface.address, {
method: 'POST',
path: '/api/v3/runs',
headers: {
connection: 'keep-alive',
'content-type': 'application/json',
'content-length': String(1024 * 1024),
},
});
assert.equal(rejected.statusCode, 401);
assert.deepEqual(rejected.body, { code: 'authentication_required' });
assert.equal(rejected.headers.connection, 'close');
});
test('streams one route-bounded body without widening the JSON body cap', async (t) => {
const observations = [];
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
maxBodyBytes: 1024,
});
t.after(() => surface.close());
const dispose = surface.installAdmission(EVIDENCE, {
async prepare(metadata) {
observations.push(`prepare:${metadata.path}`);
return {
bodyMode: 'stream',
contentType: 'application/vnd.qinglong.worker-artifact',
maximumBodyBytes: 8 * 1024,
async handleStream(body) {
observations.push(`handle:${body.contentLength}`);
let total = 0;
let chunks = 0;
for await (const chunk of body.chunks) {
total += chunk.byteLength;
chunks += 1;
}
return {
statusCode: 200,
body: { total, chunks, contentType: body.contentType },
};
},
};
},
});
t.after(() => dispose());
const bytes = Buffer.alloc(4 * 1024, 7);
const result = await request(surface.address, {
method: 'POST',
path: '/api/v3/worker-ingress/artifacts',
headers: {
'content-type': 'application/vnd.qinglong.worker-artifact',
},
rawBody: bytes,
});
assert.equal(result.statusCode, 200);
assert.deepEqual(result.body, {
total: bytes.byteLength,
chunks: 1,
contentType: 'application/vnd.qinglong.worker-artifact',
});
assert.deepEqual(observations, [
'prepare:/api/v3/worker-ingress/artifacts',
`handle:${bytes.byteLength}`,
]);
});
test('rejects invalid stream envelopes and incomplete consumption', async (t) => {
let handles = 0;
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
});
t.after(() => surface.close());
const dispose = surface.installAdmission(EVIDENCE, {
async prepare() {
return {
bodyMode: 'stream',
contentType: 'application/vnd.qinglong.worker-artifact',
maximumBodyBytes: 1024,
async handleStream() {
handles += 1;
return { statusCode: 200, body: { accepted: true } };
},
};
},
});
t.after(() => dispose());
const oversized = await request(surface.address, {
method: 'POST',
path: '/api/v3/worker-ingress/artifacts',
headers: {
'content-type': 'application/vnd.qinglong.worker-artifact',
'content-length': '1025',
},
});
assert.equal(oversized.statusCode, 413);
assert.deepEqual(oversized.body, { code: 'request_too_large' });
assert.equal(handles, 0);
const unsupported = await request(surface.address, {
method: 'POST',
path: '/api/v3/worker-ingress/artifacts',
headers: { 'content-type': 'application/octet-stream' },
rawBody: 'log',
});
assert.equal(unsupported.statusCode, 415);
assert.deepEqual(unsupported.body, { code: 'unsupported_content_type' });
assert.equal(handles, 0);
const incomplete = await request(surface.address, {
method: 'POST',
path: '/api/v3/worker-ingress/artifacts',
headers: {
'content-type': 'application/vnd.qinglong.worker-artifact',
},
rawBody: 'log',
});
assert.equal(incomplete.statusCode, 500);
assert.deepEqual(incomplete.body, { code: 'internal_error' });
assert.equal(incomplete.headers.connection, 'close');
assert.equal(handles, 1);
});
test('refunds successful admission and limits failed preflight before body reads', async (t) => {
const events = [];
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
authenticationRatePerPeer: 1,
authenticationRateGlobal: 10,
authenticationRateMaxPeers: 4,
onAuthenticationShieldEvent(event) {
events.push(event);
},
});
t.after(() => surface.close());
const beforeRead = await request(surface.address, {
path: '/api/v3/test',
headers: { 'x-forwarded-for': '198.51.100.10' },
});
assert.equal(beforeRead.statusCode, 503);
assert.deepEqual(beforeRead.body, { code: 'not_ready' });
let prepares = 0;
const dispose = surface.installAdmission(EVIDENCE, {
async prepare(metadata) {
prepares += 1;
if (metadata.path === '/api/v3/rejected') {
throw Object.assign(new Error('authentication failed'), {
statusCode: 401,
code: 'authentication_required',
});
}
return { handle: () => ({ statusCode: 204 }) };
},
});
t.after(() => dispose());
const admitted = await request(surface.address, {
path: '/api/v3/test',
headers: { 'x-forwarded-for': '198.51.100.11' },
});
assert.equal(admitted.statusCode, 204);
assert.equal(prepares, 1);
const rejected = await request(surface.address, {
path: '/api/v3/rejected',
});
assert.equal(rejected.statusCode, 401);
assert.deepEqual(rejected.body, { code: 'authentication_required' });
assert.equal(prepares, 2);
const limited = await request(surface.address, {
method: 'POST',
path: '/api/v3/rejected',
headers: {
connection: 'keep-alive',
'content-type': 'application/json',
'content-length': String(1024 * 1024),
'x-forwarded-for': '203.0.113.99',
},
});
assert.equal(limited.statusCode, 429);
assert.deepEqual(limited.body, { code: 'authentication_rate_limited' });
assert.equal(limited.headers['retry-after'], '60');
assert.equal(limited.headers.connection, 'close');
assert.equal(prepares, 2);
assert.deepEqual(events, [{ outcome: 'rate_limited', reason: 'peer' }]);
const probe = await request(surface.address, { path: '/livez' });
assert.equal(probe.statusCode, 200);
});
test('withdraws admission immediately and drains in-flight requests', async (t) => {
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
maxInFlightRequests: 1,
drainTimeoutMs: 1000,
});
t.after(() => surface.close());
let entered;
const enteredPromise = new Promise((resolve) => {
entered = resolve;
});
let release;
const gate = new Promise((resolve) => {
release = resolve;
});
const dispose = surface.installAdmission(
EVIDENCE,
pipeline(async () => {
entered();
await gate;
return { statusCode: 200, body: { completed: true } };
}),
);
const first = request(surface.address, { path: '/api/v3/slow' });
await enteredPromise;
const capacity = await request(surface.address, { path: '/api/v3/second' });
assert.equal(capacity.statusCode, 503);
assert.deepEqual(capacity.body, { code: 'admission_capacity_exhausted' });
let drained = false;
const draining = dispose().then(() => {
drained = true;
});
assert.equal(
(await request(surface.address, { path: '/readyz' })).statusCode,
503,
);
await new Promise((resolve) => setImmediate(resolve));
assert.equal(drained, false);
release();
const withdrawn = await first;
assert.equal(withdrawn.statusCode, 503);
assert.deepEqual(withdrawn.body, { code: 'admission_draining' });
await draining;
assert.equal(drained, true);
});
test('reports a drain timeout when a handler ignores cancellation', async (t) => {
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
drainTimeoutMs: 100,
});
t.after(() => surface.close());
let entered;
const enteredPromise = new Promise((resolve) => {
entered = resolve;
});
const dispose = surface.installAdmission(
EVIDENCE,
pipeline(async () => {
entered();
await new Promise(() => {});
return { statusCode: 200 };
}),
);
const response = request(surface.address, { path: '/api/v3/stuck' });
await enteredPromise;
await assert.rejects(dispose(), ClusterControlAdmissionDrainTimeoutError);
const timedOut = await response;
assert.equal(timedOut.statusCode, 503);
assert.deepEqual(timedOut.body, { code: 'admission_draining' });
});
test('rejects unsafe listener and resource configurations before binding', async () => {
const plaintext = await startClusterControlHttpSurface({ port: 0 });
await assert.rejects(
Promise.resolve().then(() => plaintext.reloadMutualTls(MTLS)),
/does not use mutual TLS/,
);
await plaintext.close();
await assert.rejects(
startClusterControlHttpSurface({
port: 0,
mutualTls: {
...MTLS,
certificateRevocationLists: [
'-----BEGIN X509 CRL-----\ninvalid\n-----END X509 CRL-----',
],
},
}),
ClusterControlHttpConfigurationError,
);
await assert.rejects(
startClusterControlHttpSurface({ host: '0.0.0.0;bad', port: 0 }),
ClusterControlHttpConfigurationError,
);
await assert.rejects(
startClusterControlHttpSurface({
port: 0,
maxInFlightRequests: 1025,
}),
ClusterControlHttpConfigurationError,
);
await assert.rejects(
startClusterControlHttpSurface({
port: 0,
authenticationRateMaxPeers: 65_537,
}),
ClusterControlHttpConfigurationError,
);
});
@@ -0,0 +1,157 @@
'use strict';
const assert = require('node:assert/strict');
const {
chmod,
mkdir,
mkdtemp,
rename,
rm,
symlink,
writeFile,
} = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const { test } = require('node:test');
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
const {
ClusterMountedSecretProvider,
ClusterMountedSecretProviderError,
clusterMountedSecretFileName,
createClusterMountedSecretProvider,
} = require('@qinglong/cluster-control/mounted-secret-provider');
const SECRET_REF = createSecretRef({
projectId: 'project-1',
name: 'api-token',
});
const VERSIONED_SECRET_REF = createSecretRef({
projectId: 'project-1',
name: 'certificate',
version: 3,
});
function authority(secretRefs = [SECRET_REF]) {
return {
workerId: 'worker-1',
workerSessionId: '018f0000-0000-7000-8000-000000000001',
workerGeneration: 1,
runId: 'run-1',
attemptId: 'attempt-1',
projectId: 'project-1',
taskId: 'task-1',
taskRevision: 'revision-1',
executionDigest: 'a'.repeat(64),
offerId: 'offer-1',
leaseGeneration: 1,
leaseVersion: 1,
secretRefs,
};
}
async function privateFile(file, value, mode = 0o400) {
await writeFile(file, value);
await chmod(file, mode);
}
test('maps canonical SecretRef to a stable path-free Kubernetes key', () => {
const first = clusterMountedSecretFileName(SECRET_REF);
assert.match(first, /^[0-9a-f]{64}$/);
assert.equal(clusterMountedSecretFileName(SECRET_REF), first);
assert.notEqual(
clusterMountedSecretFileName(VERSIONED_SECRET_REF),
first,
);
assert.throws(
() => clusterMountedSecretFileName('not-a-secret-ref'),
ClusterMountedSecretProviderError,
);
});
test('resolves every request again and observes atomic material rotation', async (t) => {
const root = await mkdtemp(path.join(os.tmpdir(), 'ql3-mounted-secret-'));
t.after(() => rm(root, { recursive: true, force: true }));
await chmod(root, 0o700);
const file = path.join(root, clusterMountedSecretFileName(SECRET_REF));
await privateFile(file, 'generation-one');
const provider = await createClusterMountedSecretProvider({
rootDirectory: root,
});
const first = await provider.resolve(authority());
assert.deepEqual(first.values, [
{ secretRef: SECRET_REF, value: 'generation-one' },
]);
await first.dispose();
const replacement = `${file}.replacement`;
await privateFile(replacement, 'generation-two');
await rename(replacement, file);
const second = await provider.resolve(authority());
assert.deepEqual(second.values, [
{ secretRef: SECRET_REF, value: 'generation-two' },
]);
await second.dispose();
await second.dispose();
});
test('accepts an in-root projected-volume symlink and rejects escapes or unsafe bytes', async (t) => {
const root = await mkdtemp(path.join(os.tmpdir(), 'ql3-projected-secret-'));
const outside = await mkdtemp(path.join(os.tmpdir(), 'ql3-outside-secret-'));
t.after(async () => {
await rm(root, { recursive: true, force: true });
await rm(outside, { recursive: true, force: true });
});
await chmod(root, 0o700);
const generation = path.join(root, '..data-generation-1');
await mkdir(generation, { mode: 0o700 });
const name = clusterMountedSecretFileName(SECRET_REF);
await privateFile(path.join(generation, name), 'projected-value', 0o440);
await symlink(path.join('..data-generation-1', name), path.join(root, name));
const provider = new ClusterMountedSecretProvider({ rootDirectory: root });
const projected = await provider.resolve(authority());
assert.equal(projected.values[0].value, 'projected-value');
await projected.dispose();
await rm(path.join(root, name));
await privateFile(path.join(outside, name), 'escaped-value');
await symlink(path.join(outside, name), path.join(root, name));
await assert.rejects(
provider.resolve(authority()),
ClusterMountedSecretProviderError,
);
await rm(path.join(root, name));
await privateFile(path.join(root, name), 'world-readable', 0o444);
await assert.rejects(
provider.resolve(authority()),
ClusterMountedSecretProviderError,
);
await rm(path.join(root, name));
await privateFile(path.join(root, name), Buffer.from([0xff]), 0o400);
await assert.rejects(
provider.resolve(authority()),
ClusterMountedSecretProviderError,
);
});
test('fails readiness for a missing or symlinked provider root', async (t) => {
const parent = await mkdtemp(path.join(os.tmpdir(), 'ql3-secret-root-'));
t.after(() => rm(parent, { recursive: true, force: true }));
const target = path.join(parent, 'target');
const link = path.join(parent, 'link');
await mkdir(target, { mode: 0o700 });
await symlink(target, link);
await assert.rejects(
createClusterMountedSecretProvider({ rootDirectory: link }),
ClusterMountedSecretProviderError,
);
await assert.rejects(
createClusterMountedSecretProvider({
rootDirectory: path.join(parent, 'missing'),
}),
ClusterMountedSecretProviderError,
);
});
@@ -0,0 +1,95 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_PLUGIN_PACKAGE_PROMPT_CATALOG_RESPONSE_SCHEMA,
createClusterControlPluginPackagePromptCatalogRoute,
} = require('@qinglong/cluster-control/prompt-routes');
function authorized(body = null) {
return {
request: {
requestId: '00000000-0000-4000-8000-000000000001',
method: 'GET',
path: '/api/v3/projects/project-1/packages/example/prompts',
query: {},
headers: {},
signal: new AbortController().signal,
body,
},
principal: {
subject: { type: 'api_app', id: 'app-1' },
authenticationId: 'credential-1',
authenticatedAtMs: 1,
expiresAtMs: 10_000,
assurance: 'service',
},
operationId: 'prompt.read',
permission: 'model.invoke',
projectId: 'project-1',
policyFence: { projectVersion: 3, bindingVersion: 7 },
};
}
test('returns a bounded content-free Prompt catalog', async () => {
let target;
const route = createClusterControlPluginPackagePromptCatalogRoute({
async inspect(projectId, packageName) {
target = { projectId, packageName };
return {
schema: CLUSTER_PLUGIN_PACKAGE_PROMPT_CATALOG_RESPONSE_SCHEMA,
projectId,
packageName,
found: true,
publicationState: 'active',
prompts: [{
id: 'summary',
name: 'Summary',
description: null,
parameters: [{ name: 'subject', description: null, required: true }],
}],
};
},
});
assert.equal(route.operationId, 'prompt.read');
assert.equal(route.permission, 'model.invoke');
const result = await route.handle(authorized(), {
projectId: 'project-1',
packageName: 'example',
});
assert.equal(result.statusCode, 200);
assert.deepEqual(target, { projectId: 'project-1', packageName: 'example' });
assert.equal(JSON.stringify(result).includes('template'), false);
});
test('rejects bodies and masks malformed or unavailable catalog state', async () => {
let calls = 0;
const route = createClusterControlPluginPackagePromptCatalogRoute({
async inspect(projectId, packageName) {
calls += 1;
return {
schema: CLUSTER_PLUGIN_PACKAGE_PROMPT_CATALOG_RESPONSE_SCHEMA,
projectId: 'another-project',
packageName,
found: false,
publicationState: null,
prompts: [],
};
},
});
assert.deepEqual(
await route.handle(authorized({ unexpected: true }), {
projectId: 'project-1',
packageName: 'example',
}),
{ statusCode: 400, body: { code: 'invalid_prompt_catalog_request' } },
);
assert.equal(calls, 0);
assert.deepEqual(
await route.handle(authorized(), {
projectId: 'project-1',
packageName: 'example',
}),
{ statusCode: 503, body: { code: 'prompt_catalog_unavailable' } },
);
});
@@ -0,0 +1,130 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
createClusterControlPluginPackagePromptExecutionInspectionRoute,
} = require('@qinglong/cluster-control/prompt-routes');
function authorized(body = null) {
return {
request: {
requestId: '00000000-0000-4000-8000-000000000001',
method: 'GET',
path: '/api/v3/projects/project-1/packages/example/prompts/summary/executions/execution-request-1',
query: {},
headers: {},
signal: new AbortController().signal,
body,
},
principal: {
subject: { type: 'user', id: 'owner-1' },
authenticationId: 'api_credential:credential-1:1',
authenticatedAtMs: 1,
expiresAtMs: 10_000,
assurance: 'multi_factor',
},
operationId: 'prompt.execution.read',
permission: 'run.read',
projectId: 'project-1',
policyFence: { projectVersion: 3, bindingVersion: 7 },
};
}
const parameters = {
projectId: 'project-1',
packageName: 'example',
promptId: 'summary',
executionRequestId: 'execution-request-1',
};
function found(command) {
return {
schema: 'qinglong/plugin-package-prompt-execution-inspection@v1',
found: true,
projectId: command.projectId,
packageName: command.packageName,
promptId: command.promptId,
executionRequestId: command.executionRequestId,
execution: {
invocationId: 'invocation-1',
runId: '00000000-0000-4000-8000-000000000010',
stepRunId: 'step-1',
runStatus: 'succeeded',
runVersion: 5,
eventSequence: 5,
stepStatus: 'succeeded',
stepVersion: 3,
admittedAtMs: 1_000,
startedAtMs: 1_000,
finishedAtMs: 1_500,
finalizedAtMs: 1_500,
},
};
}
test('reads one content-free Prompt execution by caller-known requestId', async () => {
let command;
const route = createClusterControlPluginPackagePromptExecutionInspectionRoute(
{
async inspectAuthorized(value) {
command = value;
return found(value);
},
},
{
now: () => 2_000,
createEventId: () => '00000000-0000-4000-8000-000000000002',
},
);
assert.equal(route.operationId, 'prompt.execution.read');
assert.equal(route.permission, 'run.read');
const result = await route.handle(authorized(), parameters);
assert.equal(result.statusCode, 200);
assert.equal(command.executionRequestId, 'execution-request-1');
assert.equal(command.audit.operationId, 'prompt.execution.read');
assert.equal(JSON.stringify(result).includes('template'), false);
assert.equal(JSON.stringify(result).includes('parameter'), false);
});
test('masks cross-target absence and maps an authorization fence race', async () => {
let calls = 0;
const missing = createClusterControlPluginPackagePromptExecutionInspectionRoute(
{
async inspectAuthorized(command) {
calls += 1;
return { ...found(command), found: false, execution: null };
},
},
{
now: () => 2_000,
createEventId: () => '00000000-0000-4000-8000-000000000002',
},
);
assert.deepEqual(await missing.handle(authorized({ widened: true }), parameters), {
statusCode: 400,
body: { code: 'invalid_request_body' },
});
assert.equal(calls, 0);
assert.deepEqual(await missing.handle(authorized(), parameters), {
statusCode: 404,
body: { code: 'prompt_execution_not_found' },
});
const conflict = createClusterControlPluginPackagePromptExecutionInspectionRoute(
{
async inspectAuthorized() {
throw Object.assign(new Error('drift'), {
code: 'PLUGIN_PACKAGE_PROMPT_EXECUTION_INSPECTION_AUTHORIZATION_FENCE_CONFLICT',
});
},
},
{
now: () => 2_000,
createEventId: () => '00000000-0000-4000-8000-000000000003',
},
);
assert.deepEqual(await conflict.handle(authorized(), parameters), {
statusCode: 409,
body: { code: 'authorization_fence_conflict' },
});
});
@@ -0,0 +1,132 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
createClusterControlPluginPackagePromptExecutionOutputReadRoute,
} = require('@qinglong/cluster-control/prompt-routes');
const parameters = Object.freeze({
projectId: 'project-1',
packageName: 'example',
promptId: 'summary',
executionRequestId: 'execution-request-1',
});
function authorized(body = null) {
return {
request: {
requestId: 'request-1',
method: 'GET',
path: '/unused',
query: {},
headers: {},
signal: new AbortController().signal,
body,
},
principal: {
subject: { type: 'user', id: 'owner-1' },
authenticationId: 'authentication-1',
authenticatedAtMs: 1,
expiresAtMs: 10_000,
assurance: 'multi_factor',
},
operationId: 'prompt.execution.output.read',
permission: 'artifact.read',
projectId: 'project-1',
policyFence: { projectVersion: 3, bindingVersion: 7 },
};
}
function available(command, overrides = {}) {
return {
schema: 'qinglong/plugin-package-prompt-execution-output-read-result@v1',
status: 'available',
projectId: command.projectId,
packageName: command.packageName,
promptId: command.promptId,
executionRequestId: command.executionRequestId,
reference: {
schema: 'qinglong/plugin-package-prompt-output-artifact-reference@v1',
artifactId: 'pao:0123456789abcdef0123456789abcdef',
projectId: command.projectId,
runId: '00000000-0000-4000-8000-000000000010',
stepRunId: 'step-1',
invocationId: 'invocation-1',
contentDigest: 'b'.repeat(64),
outputBytes: 14,
retentionPolicyDigest: 'c'.repeat(64),
retentionEligibleAtMs: 10_000,
keyId: 'key-1',
algorithm: 'aes-256-gcm',
artifactDigest: 'a'.repeat(64),
},
result: {
provider: 'provider-1',
model: 'model-1',
text: 'private output',
finishReason: 'stop',
usage: { inputTokens: 2, outputTokens: 3, totalTokens: 5 },
},
...overrides,
};
}
test('reads durable Prompt output by caller-known execution requestId', async () => {
let command;
const route = createClusterControlPluginPackagePromptExecutionOutputReadRoute({
async read(value) {
command = value;
return available(value);
},
});
assert.equal(route.operationId, 'prompt.execution.output.read');
assert.equal(route.permission, 'artifact.read');
const result = await route.handle(authorized(), parameters);
assert.equal(result.statusCode, 200);
assert.equal(
result.body.schema,
'qinglong/cluster-plugin-package-prompt-execution-output-read-response@v1',
);
assert.equal(result.body.result.text, 'private output');
assert.equal(command.executionRequestId, parameters.executionRequestId);
assert.equal(command.principal.subject.id, 'owner-1');
});
test('masks missing output and rejects malformed requests before capability', async () => {
let calls = 0;
const route = createClusterControlPluginPackagePromptExecutionOutputReadRoute({
async read(command) {
calls += 1;
return {
schema: 'qinglong/plugin-package-prompt-execution-output-read-result@v1',
status: 'not_found',
projectId: command.projectId,
packageName: command.packageName,
promptId: command.promptId,
executionRequestId: command.executionRequestId,
};
},
});
assert.deepEqual(await route.handle(authorized(), parameters), {
statusCode: 404,
body: { code: 'prompt_execution_output_not_found' },
});
assert.equal(calls, 1);
assert.equal(
(await route.handle(authorized({ widened: true }), parameters)).statusCode,
400,
);
assert.equal(calls, 1);
});
test('fails closed when capability widens or drifts from the exact target', async () => {
const route = createClusterControlPluginPackagePromptExecutionOutputReadRoute({
async read(command) {
return available(command, { packageName: 'another-package' });
},
});
assert.deepEqual(await route.handle(authorized(), parameters), {
statusCode: 503,
body: { code: 'prompt_execution_output_read_unavailable' },
});
});
@@ -0,0 +1,195 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_REQUEST_SCHEMA,
CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_RESPONSE_SCHEMA,
createClusterControlPluginPackagePromptExecutionRoute,
} = require('@qinglong/cluster-control/prompt-routes');
function authorized(body, overrides = {}) {
return {
request: {
requestId: '00000000-0000-4000-8000-000000000001',
method: 'POST',
path: '/api/v3/projects/project-1/packages/example/prompts/summary/executions',
query: {},
headers: {},
signal: new AbortController().signal,
body,
},
principal: {
subject: { type: 'api_app', id: 'app-1' },
authenticationId: 'credential-1',
authenticatedAtMs: 1,
expiresAtMs: 10_000,
assurance: 'service',
},
operationId: 'prompt.execute',
permission: 'model.invoke',
projectId: 'project-1',
policyFence: { projectVersion: 3, bindingVersion: 7 },
...overrides,
};
}
function body(overrides = {}) {
return {
schema: CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_REQUEST_SCHEMA,
requestId: 'prompt-request-1',
traceId: 'trace-1',
parameters: { subject: 'private input' },
provider: 'openai-compatible',
model: 'model-a',
maxOutputTokens: 512,
temperature: 0.2,
timeoutMs: 5_000,
...overrides,
};
}
function result(status = 'executed', liveResult = { text: 'private output' }) {
return {
status,
admission: {
requestId: 'prompt-request-1',
invocationId: 'ppi:1',
runId: 'ppr:1',
stepRunId: 'pps:1',
},
finalization: { runStatus: 'succeeded' },
result: liveResult,
};
}
test('builds a bounded subject- and policy-fenced execution command', async () => {
let command;
const route = createClusterControlPluginPackagePromptExecutionRoute(
{
async execute(value) {
command = value;
return result();
},
},
{
now: () => 2_000,
maxExecutionMs: 10_000,
createEventId: () => '00000000-0000-4000-8000-000000000002',
},
);
const request = authorized(body());
const response = await route.handle(request, {
projectId: 'project-1',
packageName: 'example',
promptId: 'summary',
});
assert.equal(response.statusCode, 200);
assert.equal(response.body.schema, CLUSTER_PLUGIN_PACKAGE_PROMPT_EXECUTION_RESPONSE_SCHEMA);
assert.equal(response.body.replayed, false);
assert.equal(response.body.result.text, 'private output');
assert.deepEqual(command.principal, request.principal);
assert.equal(command.auditEventId, '00000000-0000-4000-8000-000000000002');
assert.deepEqual(command.policyFence, { projectVersion: 3, bindingVersion: 7 });
assert.equal(command.plannedAtMs, 2_000);
assert.equal(command.deadlineAtMs, 7_000);
assert.equal(command.signal, request.request.signal);
assert.equal('publication' in command, false);
assert.equal('publicationDigest' in command, false);
});
test('returns an explicit content-free replay receipt', async () => {
const route = createClusterControlPluginPackagePromptExecutionRoute({
async execute() {
return result('existing', null);
},
});
const response = await route.handle(authorized(body()), {
projectId: 'project-1',
packageName: 'example',
promptId: 'summary',
});
assert.equal(response.statusCode, 200);
assert.equal(response.body.replayed, true);
assert.equal(response.body.result, null);
});
test('strictly carries durable output intent and returns only its reference on replay', async () => {
let command;
const outputArtifact = {
schema: 'qinglong/plugin-package-prompt-output-artifact-reference@v1',
artifactId: 'pao:artifact-1',
artifactDigest: 'b'.repeat(64),
};
const route = createClusterControlPluginPackagePromptExecutionRoute({
async execute(value) {
command = value;
return { ...result('existing', null), outputArtifact };
},
});
const output = {
mode: 'durable_artifact',
retentionPolicy: {
revision: 'cluster-prompt-output-v1',
retentionMs: 86_400_000,
},
};
const response = await route.handle(authorized(body({ output })), {
projectId: 'project-1',
packageName: 'example',
promptId: 'summary',
});
assert.equal(response.statusCode, 200);
assert.deepEqual(command.output, output);
assert.equal(response.body.result, null);
assert.deepEqual(response.body.outputArtifact, outputArtifact);
});
test('rejects malformed or over-timeout bodies before the capability', async () => {
let calls = 0;
const route = createClusterControlPluginPackagePromptExecutionRoute(
{ async execute() { calls += 1; return result(); } },
{ maxExecutionMs: 1_000 },
);
for (const invalid of [
body({ timeoutMs: 1_001 }),
body({ publication: {} }),
body({ publicationDigest: 'a'.repeat(64) }),
body({ parameters: { bad: 7 } }),
body({ output: { mode: 'durable_artifact', retentionPolicy: {
revision: 'cluster-prompt-output-v1', retentionMs: 1,
} } }),
body({ output: { mode: 'live_only', unexpected: true } }),
]) {
const response = await route.handle(authorized(invalid), {
projectId: 'project-1',
packageName: 'example',
promptId: 'summary',
});
assert.equal(response.statusCode, 400);
assert.deepEqual(response.body, { code: 'invalid_prompt_execution_request' });
}
assert.equal(calls, 0);
});
test('maps internal errors to low-sensitive stable transport codes', async () => {
for (const [internal, statusCode, external] of [
['PLUGIN_PACKAGE_PROMPT_ADMISSION_NOT_ALLOWED', 409, 'prompt_execution_conflict'],
['MODEL_GATEWAY_BUSY', 429, 'prompt_execution_capacity_exceeded'],
['MODEL_POLICY_DENIED', 422, 'prompt_execution_policy_rejected'],
['MODEL_INVOCATION_DEADLINE_EXCEEDED', 504, 'prompt_execution_deadline_exceeded'],
['MODEL_PROVIDER_UNAVAILABLE', 503, 'prompt_execution_unavailable'],
]) {
const route = createClusterControlPluginPackagePromptExecutionRoute({
async execute() {
throw Object.assign(new Error('private provider detail'), { code: internal });
},
});
const response = await route.handle(authorized(body()), {
projectId: 'project-1',
packageName: 'example',
promptId: 'summary',
});
assert.equal(response.statusCode, statusCode, internal);
assert.deepEqual(response.body, { code: external });
assert.equal(JSON.stringify(response).includes('private provider detail'), false);
}
});
@@ -0,0 +1,128 @@
const assert = require('node:assert/strict');
const test = require('node:test');
const {
createClusterControlPluginPackagePromptOutputReadRoute,
} = require('@qinglong/cluster-control/prompt-routes');
const DIGEST = 'a'.repeat(64);
const CONTENT_DIGEST = 'b'.repeat(64);
const RETENTION_DIGEST = 'c'.repeat(64);
const ARTIFACT_ID = 'pao:1234';
function authorized(query = { artifact_digest: [DIGEST] }, body = null) {
return {
request: {
requestId: 'request-1',
method: 'GET',
path: '/unused',
query,
headers: {},
signal: new AbortController().signal,
body,
},
principal: {
subject: { type: 'user', id: 'user-1' },
authenticationId: 'auth-1',
authenticatedAtMs: 1,
expiresAtMs: 10_000,
assurance: 'multi_factor',
},
operationId: 'prompt.output.read',
permission: 'artifact.read',
projectId: 'project-1',
policyFence: { projectVersion: 1, bindingVersion: 1 },
};
}
function available(command, overrides = {}) {
return {
schema: 'qinglong/plugin-package-prompt-output-read-result@v1',
status: 'available',
reference: {
schema: 'qinglong/plugin-package-prompt-output-artifact-reference@v1',
artifactId: command.artifactId,
projectId: command.projectId,
runId: command.runId,
stepRunId: 'step-1',
invocationId: 'invocation-1',
contentDigest: CONTENT_DIGEST,
outputBytes: 14,
retentionPolicyDigest: RETENTION_DIGEST,
retentionEligibleAtMs: 10_000,
keyId: 'key-1',
algorithm: 'aes-256-gcm',
artifactDigest: command.artifactDigest,
},
result: {
provider: 'openai-compatible',
model: 'model-1',
text: 'durable output',
finishReason: 'stop',
usage: { inputTokens: 2, outputTokens: 3, totalTokens: 5 },
},
...overrides,
};
}
test('returns one bounded Prompt output after the reviewed read capability', async () => {
let command;
const route = createClusterControlPluginPackagePromptOutputReadRoute({
async read(value) {
command = value;
return available(value);
},
});
const result = await route.handle(authorized(), {
projectId: 'project-1',
runId: 'run-1',
artifactId: ARTIFACT_ID,
});
assert.equal(result.statusCode, 200);
assert.equal(result.body.schema,
'qinglong/cluster-plugin-package-prompt-output-read-response@v1');
assert.equal(result.body.result.text, 'durable output');
assert.equal(command.principal.subject.id, 'user-1');
assert.equal(command.artifactDigest, DIGEST);
});
test('masks product not-found and rejects malformed requests before capability', async () => {
let calls = 0;
const route = createClusterControlPluginPackagePromptOutputReadRoute({
async read() {
calls += 1;
return {
schema: 'qinglong/plugin-package-prompt-output-read-result@v1',
status: 'not_found',
};
},
});
assert.equal((await route.handle(authorized(), {
projectId: 'project-1', runId: 'run-1', artifactId: ARTIFACT_ID,
})).statusCode, 404);
assert.equal(calls, 1);
assert.equal((await route.handle(authorized({ artifact_digest: ['bad'] }), {
projectId: 'project-1', runId: 'run-1', artifactId: ARTIFACT_ID,
})).statusCode, 400);
assert.equal(calls, 1);
});
test('fails closed when capability widens or drifts from the requested identity', async () => {
const route = createClusterControlPluginPackagePromptOutputReadRoute({
async read(command) {
return available(command, {
reference: {
...available(command).reference,
projectId: 'project-other',
},
});
},
});
const result = await route.handle(authorized(), {
projectId: 'project-1', runId: 'run-1', artifactId: ARTIFACT_ID,
});
assert.equal(result.statusCode, 503);
assert.deepEqual(result.body, { code: 'prompt_output_read_unavailable' });
});
@@ -0,0 +1,819 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
createInitialPluginPackageAutomationPublication,
} = require('@qinglong/runtime-core/plugin-package-automation-publication');
const {
createPluginPackageWorkflowAdmissionBundle,
} = require('@qinglong/runtime-core/plugin-package-workflow-execution-plan');
const {
pluginPackageTaskReconciliationFixture,
} = require('../../../test/contracts/pluginPackageTaskReconciliationRepositoryContract.cjs');
const {
createClusterPluginPackageWorkflowAdministrationCapability,
} = require('@qinglong/cluster-control/workflow-administration');
const {
createClusterControlPluginPackageWorkflowRoutes,
} = require('@qinglong/cluster-control/workflow-routes');
const IDS = Object.freeze({
planId: '123e4567-e89b-42d3-a456-426614174000',
runId: '123e4567-e89b-42d3-a456-426614174001',
collect: '123e4567-e89b-42d3-a456-426614174002',
summarize: '123e4567-e89b-42d3-a456-426614174003',
});
function productFixture() {
const value = pluginPackageTaskReconciliationFixture('cluster-product', {
workflows: [
{
schema: 'qinglong/plugin-package-workflow-resource@v1',
id: 'daily',
name: 'Daily workflow',
enabled: true,
steps: [
{ id: 'collect', task: 'alpha', needs: [] },
{ id: 'summarize', task: 'beta', needs: ['collect'] },
],
},
],
});
return {
...value,
publication: createInitialPluginPackageAutomationPublication(
value.revision,
value.registry,
2_000,
),
};
}
function principal(now = 3_000) {
return Object.freeze({
subject: { type: 'api_app', id: 'workflow-operator' },
authenticationId: 'api_credential:workflow-product:1',
authenticatedAtMs: now - 1,
expiresAtMs: now + 60_000,
assurance: 'service',
});
}
test('derives the cluster Workflow plan from current server publications and exactly replays it', async () => {
const value = productFixture();
let storedPlan = null;
let cancellationCommand;
let inspectionCommand;
let runListCommand;
let stepListCommand;
let runEventListCommand;
const admissions = [];
const capability = createClusterPluginPackageWorkflowAdministrationCapability(
{
async findCurrent(projectId, packageName) {
assert.equal(projectId, value.projectId);
assert.equal(packageName, value.packageName);
return value.publication;
},
},
{
async find(generationDigest) {
assert.equal(
generationDigest,
value.revision.generation.generationDigest,
);
return value.revision;
},
},
{
async findPlanByPlanId(planId) {
assert.equal(planId, IDS.planId);
return storedPlan;
},
async admitAuthorized(admission) {
admissions.push(admission);
const bundle = createPluginPackageWorkflowAdmissionBundle(
admission.plan,
);
const status = storedPlan ? 'existing' : 'created';
storedPlan = admission.plan;
return { status, receipt: bundle.receipt };
},
},
{
async inspectRunAuthorized(command) {
inspectionCommand = command;
return {
schema: 'qinglong/plugin-package-workflow-run-inspection@v1',
found: false,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
run: null,
stepCount: null,
stepStatusCounts: null,
};
},
},
{
async listRunsAuthorized(command) {
runListCommand = command;
return {
schema: 'qinglong/plugin-package-workflow-run-list@v1',
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
after: command.after,
runs: [],
truncated: false,
next: null,
};
},
},
{
async listStepRunsAuthorized(command) {
stepListCommand = command;
return {
schema: 'qinglong/plugin-package-workflow-step-run-list@v1',
found: false,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
stepRuns: [],
truncated: false,
next: null,
};
},
},
{
async listRunEventsAuthorized(command) {
runEventListCommand = command;
return {
schema: 'qinglong/plugin-package-workflow-run-event-list@v1',
found: false,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
afterSequence: command.afterSequence,
headSequence: null,
events: [],
truncated: false,
nextAfterSequence: null,
};
},
},
{
async requestUserCancellation(command) {
cancellationCommand = command;
return {
status: 'accepted',
projectId: command.projectId,
runId: command.runId,
runStatus: 'running',
runVersion: 4,
eventSequence: 4,
cancelRequestedAtMs: 3_100,
cancelReason: 'user',
};
},
},
value.registry,
);
const listed = await capability.inspect(value.projectId, value.packageName);
assert.equal(listed.found, true);
assert.deepEqual(listed.workflows[0].steps, [
{ id: 'collect', task: 'alpha', needs: [] },
{ id: 'summarize', task: 'beta', needs: ['collect'] },
]);
const command = {
projectId: value.projectId,
packageName: value.packageName,
workflowId: 'daily',
planId: IDS.planId,
runId: IDS.runId,
stepRunIds: { collect: IDS.collect, summarize: IDS.summarize },
principal: principal(),
policyFence: { projectVersion: 3, bindingVersion: 7 },
plannedAtMs: 3_000,
};
const created = await capability.start(command);
const replay = await capability.start({ ...command, plannedAtMs: 9_000 });
assert.equal(created.status, 'created');
assert.equal(replay.status, 'existing');
assert.equal(replay.plan.planDigest, created.plan.planDigest);
assert.equal(
created.plan.target.publicationDigest,
value.publication.publicationDigest,
);
assert.equal(admissions[0].audit.eventId, IDS.planId);
assert.equal(admissions[0].audit.requestId, IDS.planId);
assert.equal(
admissions[0].audit.authenticationId,
principal().authenticationId,
);
assert.equal(admissions[1].audit.occurredAtMs, created.plan.plannedAtMs);
assert.equal(
(
await capability.cancel({
projectId: value.projectId,
packageName: value.packageName,
workflowId: 'daily',
runId: IDS.runId,
mutationId: 'workflow-cancel-mutation-1',
eventId: '018f0000-0000-7000-8000-000000000091',
principal: principal(),
policyFence: { projectVersion: 3, bindingVersion: 7 },
})
).status,
'accepted',
);
assert.deepEqual(cancellationCommand.workflowTarget, {
packageName: value.packageName,
workflowId: 'daily',
});
const inspected = await capability.inspectRun({
projectId: value.projectId,
packageName: value.packageName,
workflowId: 'daily',
runId: IDS.runId,
requestId: 'workflow-run-read-request-1',
auditEventId: '123e4567-e89b-42d3-a456-426614174093',
principal: principal(),
policyFence: { projectVersion: 3, bindingVersion: 7 },
observedAtMs: 3_200,
});
assert.equal(inspected.found, false);
assert.equal(inspectionCommand.audit.operationId, 'workflow.run.read');
assert.equal(
inspectionCommand.audit.requestId,
'workflow-run-read-request-1',
);
assert.deepEqual(inspectionCommand.fence, {
projectVersion: 3,
bindingVersion: 7,
});
const runPage = await capability.listRuns({
projectId: value.projectId,
packageName: value.packageName,
workflowId: 'daily',
limit: 16,
after: { admittedAtMs: 3_100, runId: IDS.runId },
requestId: 'workflow-run-list-request-1',
auditEventId: '123e4567-e89b-42d3-a456-426614174096',
principal: principal(),
policyFence: { projectVersion: 3, bindingVersion: 7 },
observedAtMs: 3_250,
});
assert.deepEqual(runPage.runs, []);
assert.equal(runListCommand.audit.operationId, 'workflow.run.list');
assert.equal(runListCommand.limit, 16);
assert.deepEqual(runListCommand.after, {
admittedAtMs: 3_100,
runId: IDS.runId,
});
const stepPage = await capability.listStepRuns({
projectId: value.projectId,
packageName: value.packageName,
workflowId: 'daily',
runId: IDS.runId,
limit: 16,
after: { stepKey: 'collect', id: IDS.collect },
requestId: 'workflow-step-list-request-1',
auditEventId: '123e4567-e89b-42d3-a456-426614174094',
principal: principal(),
policyFence: { projectVersion: 3, bindingVersion: 7 },
observedAtMs: 3_300,
});
assert.equal(stepPage.found, false);
assert.equal(stepListCommand.audit.operationId, 'workflow.step.list');
assert.equal(stepListCommand.limit, 16);
assert.deepEqual(stepListCommand.after, {
stepKey: 'collect',
id: IDS.collect,
});
const eventPage = await capability.listRunEvents({
projectId: value.projectId,
packageName: value.packageName,
workflowId: 'daily',
runId: IDS.runId,
limit: 16,
afterSequence: 2,
requestId: 'workflow-event-list-request-1',
auditEventId: '123e4567-e89b-42d3-a456-426614174095',
principal: principal(),
policyFence: { projectVersion: 3, bindingVersion: 7 },
observedAtMs: 3_400,
});
assert.equal(eventPage.found, false);
assert.equal(runEventListCommand.audit.operationId, 'workflow.event.list');
assert.equal(runEventListCommand.limit, 16);
assert.equal(runEventListCommand.afterSequence, 2);
});
test('publishes strict authenticated list and content-free start routes', async () => {
let startCommand;
let cancellationError;
const routes = createClusterControlPluginPackageWorkflowRoutes(
{
async inspect() {
return { found: false, publicationState: null, workflows: [] };
},
async inspectRun(command) {
return {
schema: 'qinglong/plugin-package-workflow-run-inspection@v1',
found: true,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
run: {
status: 'running',
version: 4,
eventSequence: 4,
createdAtMs: 3_000,
queuedAtMs: 3_010,
startedAtMs: 3_020,
finishedAtMs: null,
cancelRequestedAtMs: null,
cancelReason: null,
},
stepCount: 1,
stepStatusCounts: {
pending: 0,
ready: 0,
waiting_approval: 0,
running: 1,
lost: 0,
succeeded: 0,
failed: 0,
skipped: 0,
cancelled: 0,
timed_out: 0,
},
};
},
async listRuns(command) {
return {
schema: 'qinglong/plugin-package-workflow-run-list@v1',
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
after: command.after,
runs: [
{
runId: IDS.runId,
status: 'running',
version: 4,
eventSequence: 4,
stepCount: 2,
admittedAtMs: 3_000,
queuedAtMs: 3_010,
startedAtMs: 3_020,
finishedAtMs: null,
cancelRequestedAtMs: null,
cancelReason: null,
},
],
truncated: true,
next: { admittedAtMs: 3_000, runId: IDS.runId },
};
},
async listStepRuns(command) {
return {
schema: 'qinglong/plugin-package-workflow-step-run-list@v1',
found: true,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
stepRuns: [
{
id: IDS.collect,
parentStepRunId: null,
stepKey: 'collect',
kind: 'task',
required: true,
status: 'running',
version: 2,
attemptCount: 1,
readyAtMs: 3_010,
startedAtMs: 3_020,
finishedAtMs: null,
resultCode: null,
createdAtMs: 3_000,
updatedAtMs: 3_020,
},
],
truncated: true,
next: { stepKey: 'collect', id: IDS.collect },
};
},
async listRunEvents(command) {
return {
schema: 'qinglong/plugin-package-workflow-run-event-list@v1',
found: true,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
afterSequence: command.afterSequence,
headSequence: 4,
events: [
{
id: '123e4567-e89b-42d3-a456-426614174004',
sequence: 3,
type: 'workflow.task_attempt.running',
stepRunId: IDS.collect,
createdAtMs: 3_020,
},
],
truncated: true,
nextAfterSequence: 3,
};
},
async start(command) {
startCommand = command;
return {
status: 'created',
plan: { planId: command.planId, runId: command.runId },
receipt: { receiptDigest: 'a'.repeat(64) },
};
},
async cancel(command) {
if (cancellationError) {
throw cancellationError;
}
return {
status: 'accepted',
projectId: command.projectId,
runId: command.runId,
runStatus: 'running',
runVersion: 4,
eventSequence: 4,
cancelRequestedAtMs: 3_100,
cancelReason: 'user',
};
},
},
() => 3_000,
() => '123e4567-e89b-42d3-a456-426614174092',
);
const start = routes.find(
({ operationId }) => operationId === 'workflow.start',
);
assert.ok(start);
const authorized = {
request: {
requestId: 'transport-request-1',
body: {
schema: 'qinglong/cluster-plugin-package-workflow-start-request@v1',
planId: IDS.planId,
runId: IDS.runId,
stepRunIds: { collect: IDS.collect },
},
signal: new AbortController().signal,
},
principal: principal(),
operationId: 'workflow.start',
permission: 'run.start',
projectId: 'project-1',
policyFence: { projectVersion: 3, bindingVersion: 7 },
};
const result = await start.handle(authorized, {
packageName: 'example',
workflowId: 'daily',
});
assert.equal(result.statusCode, 201);
assert.deepEqual(Object.keys(result.body).sort(), [
'planId',
'receiptDigest',
'replayed',
'runId',
'schema',
'status',
]);
assert.equal(startCommand.principal.subject.id, 'workflow-operator');
assert.equal(startCommand.plannedAtMs, 3_000);
const inspectRun = routes.find(
({ operationId }) => operationId === 'workflow.run.read',
);
assert.ok(inspectRun);
assert.equal(inspectRun.permission, 'run.read');
const inspected = await inspectRun.handle(
{
...authorized,
operationId: 'workflow.run.read',
permission: 'run.read',
request: { ...authorized.request, body: null },
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(inspected.statusCode, 200);
assert.equal(
inspected.body.schema,
'qinglong/plugin-package-workflow-run-inspection@v1',
);
assert.deepEqual(Object.keys(inspected.body.run).sort(), [
'cancelReason',
'cancelRequestedAtMs',
'createdAtMs',
'eventSequence',
'finishedAtMs',
'queuedAtMs',
'startedAtMs',
'status',
'version',
]);
const listRuns = routes.find(
({ operationId }) => operationId === 'workflow.run.list',
);
assert.ok(listRuns);
assert.equal(listRuns.permission, 'run.read');
assert.deepEqual(listRuns.allowedQuery, [
'after_admitted_at_ms',
'after_run_id',
'limit',
]);
const listedRuns = await listRuns.handle(
{
...authorized,
operationId: 'workflow.run.list',
permission: 'run.read',
request: {
...authorized.request,
body: null,
query: {
limit: ['1'],
after_admitted_at_ms: ['3100'],
after_run_id: [IDS.runId],
},
},
},
{ packageName: 'example', workflowId: 'daily' },
);
assert.equal(listedRuns.statusCode, 200);
assert.equal(
listedRuns.body.schema,
'qinglong/plugin-package-workflow-run-list@v1',
);
assert.deepEqual(Object.keys(listedRuns.body.runs[0]).sort(), [
'admittedAtMs',
'cancelReason',
'cancelRequestedAtMs',
'eventSequence',
'finishedAtMs',
'queuedAtMs',
'runId',
'startedAtMs',
'status',
'stepCount',
'version',
]);
const invalidRunCursor = await listRuns.handle(
{
...authorized,
request: {
...authorized.request,
body: null,
query: { after_admitted_at_ms: ['3100'] },
},
},
{ packageName: 'example', workflowId: 'daily' },
);
assert.equal(invalidRunCursor.statusCode, 400);
const listSteps = routes.find(
({ operationId }) => operationId === 'workflow.step.list',
);
assert.ok(listSteps);
assert.equal(listSteps.permission, 'run.read');
assert.deepEqual(listSteps.allowedQuery, [
'after_step_key',
'after_step_run_id',
'limit',
]);
const listedSteps = await listSteps.handle(
{
...authorized,
operationId: 'workflow.step.list',
permission: 'run.read',
request: {
...authorized.request,
body: null,
query: {
limit: ['1'],
after_step_key: ['collect'],
after_step_run_id: [IDS.collect],
},
},
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(listedSteps.statusCode, 200);
assert.equal(
listedSteps.body.schema,
'qinglong/plugin-package-workflow-step-run-list@v1',
);
assert.deepEqual(Object.keys(listedSteps.body.stepRuns[0]).sort(), [
'attemptCount',
'createdAtMs',
'finishedAtMs',
'id',
'kind',
'parentStepRunId',
'readyAtMs',
'required',
'resultCode',
'startedAtMs',
'status',
'stepKey',
'updatedAtMs',
'version',
]);
const invalidStepCursor = await listSteps.handle(
{
...authorized,
request: {
...authorized.request,
body: null,
query: { after_step_key: ['collect'] },
},
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(invalidStepCursor.statusCode, 400);
const listEvents = routes.find(
({ operationId }) => operationId === 'workflow.event.list',
);
assert.ok(listEvents);
assert.equal(listEvents.permission, 'run.read');
assert.deepEqual(listEvents.allowedQuery, ['after_sequence', 'limit']);
const listedEvents = await listEvents.handle(
{
...authorized,
operationId: 'workflow.event.list',
permission: 'run.read',
request: {
...authorized.request,
body: null,
query: { limit: ['1'], after_sequence: ['2'] },
},
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(listedEvents.statusCode, 200);
assert.equal(
listedEvents.body.schema,
'qinglong/plugin-package-workflow-run-event-list@v1',
);
assert.deepEqual(Object.keys(listedEvents.body.events[0]).sort(), [
'createdAtMs',
'id',
'sequence',
'stepRunId',
'type',
]);
const invalidEventCursor = await listEvents.handle(
{
...authorized,
request: {
...authorized.request,
body: null,
query: { after_sequence: ['02'] },
},
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(invalidEventCursor.statusCode, 400);
const invalid = await start.handle(
{
...authorized,
request: {
...authorized.request,
body: { ...authorized.request.body, extra: true },
},
},
{ packageName: 'example', workflowId: 'daily' },
);
assert.equal(invalid.statusCode, 400);
const cancellation = routes.find(
({ operationId }) => operationId === 'workflow.cancel',
);
assert.ok(cancellation);
const cancelled = await cancellation.handle(
{
...authorized,
operationId: 'workflow.cancel',
permission: 'run.stop',
request: {
...authorized.request,
body: {
schema: 'qinglong/run-cancellation@v1',
mutationId: 'workflow-cancel-mutation-1',
},
},
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(cancelled.statusCode, 202);
assert.equal(cancelled.body.status, 'accepted');
assert.deepEqual(
Object.keys(cancelled.body).sort(),
[
'cancelReason',
'cancelRequestedAtMs',
'eventSequence',
'projectId',
'runId',
'runStatus',
'runVersion',
'schema',
'status',
].sort(),
);
const invalidCancellation = await cancellation.handle(
{
...authorized,
operationId: 'workflow.cancel',
permission: 'run.stop',
request: {
...authorized.request,
body: {
schema: 'qinglong/run-cancellation@v1',
mutationId: 'workflow-cancel-mutation-1',
reason: 'shutdown',
},
},
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(invalidCancellation.statusCode, 400);
cancellationError = Object.assign(new Error('private adapter detail'), {
code: 'CLUSTER_RUN_CANCELLATION_FENCE_REJECTED',
reason: 'private_adapter_detail',
});
const closedCancellationFailure = await cancellation.handle(
{
...authorized,
operationId: 'workflow.cancel',
permission: 'run.stop',
request: {
...authorized.request,
body: {
schema: 'qinglong/run-cancellation@v1',
mutationId: 'workflow-cancel-mutation-2',
},
},
},
{
packageName: 'example',
workflowId: 'daily',
runId: IDS.runId,
},
);
assert.equal(closedCancellationFailure.statusCode, 409);
assert.deepEqual(closedCancellationFailure.body, {
code: 'workflow_cancellation_fence_rejected',
reason: 'state_mismatch',
});
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,362 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterControlProcessError,
runProductionClusterControlProcess,
} = require('@qinglong/cluster-control/process');
const BASE_ENV = Object.freeze({
QL3_CLUSTER_CONTROL_ENABLED: 'true',
QL_DEPLOYMENT_PROFILE: 'cluster-control',
QL3_CLUSTER_REPLICA_ID: 'cluster-control-0',
QL3_POSTGRES_RUNTIME_URL:
'postgresql://ql3_runtime:do-not-log@postgres-rw.internal:5432/qinglong',
QL3_POSTGRES_TLS_SERVERNAME: 'postgres-rw.internal',
QL3_API_CREDENTIAL_PEPPER: 'A'.repeat(43),
});
const NEVER_UNAVAILABLE = new Promise(() => {});
function signalSource(events, signal = 'SIGTERM') {
return {
subscribe(listener) {
events.push('subscribe');
queueMicrotask(() => listener(signal));
return () => events.push('unsubscribe');
},
};
}
test('runs one production replica and drains it on the first signal', async () => {
const events = [];
const facts = [];
const result = await runProductionClusterControlProcess({
environment: BASE_ENV,
signals: signalSource(events),
emit(record) {
facts.push(record);
},
async start(options) {
events.push('start');
assert.equal(options.config.enabled, true);
assert.equal(options.config.profile, 'cluster-control');
assert.equal(options.recovery.ownerId, 'cluster-control-0');
assert.equal(options.scheduler.ownerId, 'cluster-control-0');
await options.audit({
state: 'active',
contractName: 'qinglong-cluster-control',
contractVersion: 16,
serverMajor: 18,
migrationCount: 16,
});
options.scheduler.onDiagnostic(
Object.assign(new Error('must-not-be-logged'), {
code: 'ECONNRESET',
}),
);
return {
status: 'active',
address: { host: '0.0.0.0', port: 5800 },
evidence: {
contractName: 'qinglong-cluster-control',
contractVersion: 16,
serverMajor: 18,
migrationIds: [],
},
recovery: { safe: true, remaining: 0, failed: 0 },
unavailable: NEVER_UNAVAILABLE,
availabilityStatus: () => 'ready',
async stop() {
events.push('stop');
return 'stopped';
},
};
},
});
assert.equal(result, 'stopped');
assert.deepEqual(events, ['subscribe', 'start', 'stop', 'unsubscribe']);
assert.equal(facts.some((fact) => fact.event === 'activation'), true);
assert.equal(facts.some((fact) => fact.event === 'listening'), true);
assert.equal(
facts.some(
(fact) =>
fact.event === 'shutdown_requested' && fact.signal === 'SIGTERM',
),
true,
);
assert.equal(facts.at(-1).event, 'stopped');
assert.equal(facts.at(-1).stopResult, 'stopped');
const serialized = JSON.stringify(facts);
assert.equal(serialized.includes('do-not-log'), false);
assert.equal(serialized.includes(BASE_ENV.QL3_API_CREDENTIAL_PEPPER), false);
assert.equal(serialized.includes('must-not-be-logged'), false);
assert.equal(serialized.includes('ECONNRESET'), true);
});
test('fails closed before startup for a disabled profile or invalid replica id', async () => {
let starts = 0;
for (const environment of [
{
QL3_CLUSTER_CONTROL_ENABLED: 'false',
QL_DEPLOYMENT_PROFILE: 'standalone',
},
{ ...BASE_ENV, QL3_CLUSTER_REPLICA_ID: 'unsafe replica' },
]) {
await assert.rejects(
runProductionClusterControlProcess({
environment,
signals: { subscribe() { return () => {}; } },
emit() {},
async start() {
starts += 1;
throw new Error('must not start');
},
}),
ClusterControlProcessError,
);
}
assert.equal(starts, 0);
});
test('propagates timed-out drain and always releases signal ownership', async () => {
const events = [];
const result = await runProductionClusterControlProcess({
environment: BASE_ENV,
signals: signalSource(events, 'SIGINT'),
emit() {},
async start() {
return {
status: 'active',
address: { host: '127.0.0.1', port: 5800 },
evidence: {
contractName: 'qinglong-cluster-control',
contractVersion: 16,
serverMajor: 18,
migrationIds: [],
},
recovery: { safe: true, remaining: 0, failed: 0 },
unavailable: NEVER_UNAVAILABLE,
availabilityStatus: () => 'ready',
async stop() {
events.push('stop');
return 'timed_out';
},
};
},
});
assert.equal(result, 'timed_out');
assert.deepEqual(events, ['subscribe', 'stop', 'unsubscribe']);
});
test('starts the optional Worker listener and closes its lazy Artifact binding', async () => {
const events = [];
const facts = [];
const artifactStore = {
async put() {},
async inspect() {},
};
const environment = {
...BASE_ENV,
QL3_WORKER_INGRESS_ENABLED: 'true',
QL3_POSTGRES_WORKER_INGRESS_URL:
'postgresql://ql3_worker_ingress:secret@postgres-rw.internal:5432/qinglong',
QL3_WORKER_INGRESS_POSTGRES_TLS_SERVERNAME: 'postgres-rw.internal',
QL3_WORKER_CREDENTIAL_PEPPER: 'A'.repeat(43),
QL3_WORKER_INGRESS_TLS_PRIVATE_KEY_FILE: '/run/worker/tls.key',
QL3_WORKER_INGRESS_TLS_CERTIFICATE_FILE: '/run/worker/tls.crt',
QL3_WORKER_INGRESS_TLS_CLIENT_CA_FILE: '/run/worker/client-ca.crt',
QL3_WORKER_ARTIFACT_S3_BUCKET: 'qinglong-worker-artifacts',
QL3_WORKER_ARTIFACT_S3_REGION: 'us-east-1',
};
const result = await runProductionClusterControlProcess({
environment,
signals: signalSource(events),
emit(record) {
facts.push(record);
},
async createWorkerArtifactBinding(config) {
events.push(`artifact:${config.bucket}:${config.region}`);
return {
store: artifactStore,
async close() {
events.push('close-artifact');
},
};
},
async start(options) {
events.push('start');
assert.equal(options.workerIngress.config.enabled, true);
assert.equal(options.workerIngress.artifactStore, artifactStore);
return {
status: 'active',
address: { host: '0.0.0.0', port: 5800 },
evidence: {
contractName: 'qinglong-cluster-control',
contractVersion: 16,
serverMajor: 18,
migrationIds: [],
},
recovery: { safe: true, remaining: 0, failed: 0 },
unavailable: NEVER_UNAVAILABLE,
availabilityStatus: () => 'ready',
async stop() {
events.push('stop');
return 'stopped';
},
};
},
});
assert.equal(result, 'stopped');
assert.deepEqual(events, [
'subscribe',
'artifact:qinglong-worker-artifacts:us-east-1',
'start',
'stop',
'unsubscribe',
'close-artifact',
]);
assert.equal(
facts.some((fact) => fact.event === 'worker_ingress_listening'),
true,
);
});
test('creates the configured mounted Secret provider before Worker activation', async () => {
const events = [];
const artifactStore = {
async put() {},
async inspect() {},
};
const provider = { async resolve() {} };
const result = await runProductionClusterControlProcess({
environment: {
...BASE_ENV,
QL3_WORKER_INGRESS_ENABLED: 'true',
QL3_POSTGRES_WORKER_INGRESS_URL:
'postgresql://ql3_worker_ingress:secret@postgres-rw.internal:5432/qinglong',
QL3_WORKER_INGRESS_POSTGRES_TLS_SERVERNAME: 'postgres-rw.internal',
QL3_WORKER_CREDENTIAL_PEPPER: 'A'.repeat(43),
QL3_WORKER_INGRESS_TLS_PRIVATE_KEY_FILE: '/run/worker/tls.key',
QL3_WORKER_INGRESS_TLS_CERTIFICATE_FILE: '/run/worker/tls.crt',
QL3_WORKER_INGRESS_TLS_CLIENT_CA_FILE: '/run/worker/client-ca.crt',
QL3_WORKER_ARTIFACT_S3_BUCKET: 'qinglong-worker-artifacts',
QL3_WORKER_ARTIFACT_S3_REGION: 'us-east-1',
QL3_WORKER_SECRET_PROVIDER: 'mounted-files',
QL3_WORKER_SECRET_ROOT_DIRECTORY: '/run/worker/values',
},
signals: signalSource(events),
emit() {},
async createWorkerArtifactBinding() {
events.push('artifact');
return {
store: artifactStore,
async close() {
events.push('close-artifact');
},
};
},
async createWorkerSecretProvider(config) {
events.push(`secret:${config.provider}:${config.rootDirectory}`);
return provider;
},
async start(options) {
events.push('start');
assert.equal(options.workerIngress.secretProvider, provider);
return {
status: 'active',
address: { host: '0.0.0.0', port: 5800 },
evidence: {
contractName: 'qinglong-cluster-control',
contractVersion: 16,
serverMajor: 18,
migrationIds: [],
},
recovery: { safe: true, remaining: 0, failed: 0 },
unavailable: NEVER_UNAVAILABLE,
availabilityStatus: () => 'ready',
async stop() {
events.push('stop');
return 'stopped';
},
};
},
});
assert.equal(result, 'stopped');
assert.deepEqual(events, [
'subscribe',
'artifact',
'secret:mounted-files:/run/worker/values',
'start',
'stop',
'unsubscribe',
'close-artifact',
]);
});
test('fails the process after a database fence drains the active application', async () => {
const events = [];
const facts = [];
let reportUnavailable;
const unavailable = new Promise((resolve) => {
reportUnavailable = resolve;
});
const running = runProductionClusterControlProcess({
environment: BASE_ENV,
signals: {
subscribe() {
events.push('subscribe');
return () => events.push('unsubscribe');
},
},
emit(record) {
facts.push(record);
},
async start() {
queueMicrotask(() =>
reportUnavailable(
Object.assign(new Error('must-not-escape-database-detail'), {
code: '57P01',
}),
),
);
return {
status: 'active',
address: { host: '127.0.0.1', port: 5800 },
evidence: {
contractName: 'qinglong-cluster-control',
contractVersion: 52,
serverMajor: 18,
migrationIds: [],
},
recovery: { safe: true, remaining: 0, failed: 0 },
unavailable,
availabilityStatus: () => 'unavailable',
async stop() {
events.push('stop');
return 'stopped';
},
};
},
});
await assert.rejects(
running,
(error) => error?.code === 'CLUSTER_CONTROL_DATABASE_UNAVAILABLE',
);
assert.deepEqual(events, ['subscribe', 'stop', 'unsubscribe']);
assert.equal(
facts.some(
({ event, diagnostic }) =>
event === 'database_unavailable' &&
diagnostic?.scope === 'database' &&
diagnostic?.code === '57P01',
),
true,
);
assert.equal(facts.some(({ event }) => event === 'shutdown_requested'), false);
assert.equal(facts.at(-1).event, 'stopped');
assert.equal(
JSON.stringify(facts).includes('must-not-escape-database-detail'),
false,
);
});
@@ -0,0 +1,896 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
PRODUCTION_CLUSTER_CONTROL_ROUTE_OPERATIONS,
PRODUCTION_CLUSTER_CONTROL_OPTIONAL_ROUTE_OPERATIONS,
createProductionClusterControlApplicationStack,
startProductionClusterControlApplication,
} = require('@qinglong/cluster-control/production');
const {
createTaskDefinitionRecord,
} = require('@qinglong/runtime-core/task-definition');
const EVENT_ID = '123e4567-e89b-42d3-a456-426614174092';
function metadata(path, method = 'GET', body = null, query = {}) {
return {
method,
path,
query,
headers: Object.freeze({ authorization: 'Bearer test' }),
remoteAddress: '127.0.0.1',
requestId: 'request-production-1',
signal: new AbortController().signal,
body,
};
}
function fixture(overrides = {}) {
const events = [];
const currentTask = createTaskDefinitionRecord(
{
projectId: 'project-1',
taskId: 'task-1',
expectedRevision: null,
mutationId: '123e4567-e89b-42d3-a456-426614174093',
name: 'Task 1',
description: 'private',
kind: 'command',
spec: {
schema: 'qinglong/command@v1',
config: { command: { kind: 'shell', command: 'private' } },
},
labels: { private: 'value' },
enabled: true,
occurredAtMs: 20,
},
10,
);
const input = {
evidence: {
contractName: 'qinglong-cluster-control',
contractVersion: 14,
serverMajor: 16,
migrationIds: [],
},
authenticator: {
authenticate() {
events.push('authenticate');
const now = Date.now();
return {
subject: { type: 'api_app', id: 'app-production' },
authenticationId: 'credential-production',
authenticatedAtMs: now - 1_000,
expiresAtMs: now + 60_000,
assurance: 'service',
};
},
},
policies: {
async resolve() {
events.push('authorize');
return {
project: {
id: 'project-1',
name: 'Production Project',
slug: 'production-project',
status: 'active',
version: 3,
createdAtMs: 1,
updatedAtMs: 2,
},
binding: {
projectId: 'project-1',
subject: { type: 'api_app', id: 'app-production' },
state: 'active',
role: 'owner',
version: 7,
mutationId: 'binding-production-1',
changedBy: { type: 'system', id: 'bootstrap' },
createdAtMs: 1,
},
};
},
},
runs: {
async findRunById(runId) {
events.push(`read:${runId}`);
return {
id: runId,
projectId: 'project-1',
taskId: 'task-1',
taskRevision: 'revision-1',
status: 'queued',
version: 2,
eventSequence: 1,
priority: 0,
executionOrigin: 'scheduled_system',
executionOwner: 'runtime',
createdAtMs: 1,
queuedAtMs: 2,
};
},
async listRunsByProject(query) {
events.push(`list:${query.projectId}:${query.limit}`);
return [
{
id: 'run-1',
projectId: query.projectId,
taskId: 'task-1',
taskRevision: 'revision-1',
status: 'queued',
version: 2,
eventSequence: 1,
priority: 0,
executionOrigin: 'scheduled_system',
executionOwner: 'runtime',
createdAtMs: 1,
queuedAtMs: 2,
},
];
},
async listEvents(runId, input) {
events.push(`events:${runId}:${input.afterSequence}:${input.limit}`);
return [
{
id: 'event-1',
runId,
sequence: 1,
type: 'run.created',
actorType: 'system',
payload: { secret: 'must-not-cross-projection' },
createdAtMs: 2,
},
];
},
},
trustedToolStorage: {
stepRuns: {
async listByRun() {
events.push('steps');
return { stepRuns: [], truncated: false };
},
},
},
runCancellation: {
async requestUserCancellation(command) {
events.push(`cancel:${command.runId}:${command.eventId}`);
return {
status: 'accepted',
projectId: command.projectId,
runId: command.runId,
cancelReason: 'user',
cancelRequestedAtMs: 20,
runStatus: 'queued',
runVersion: 3,
eventSequence: 2,
};
},
},
taskStart: {
async startTask(command) {
events.push(`task-start:${command.taskId}:${command.mutationId}`);
return {
status: 'accepted',
projectId: command.projectId,
taskId: command.taskId,
taskRevision: command.expectedRevision,
taskContentDigest: command.expectedContentDigest,
runId: command.runId,
attemptId: command.attemptId,
runStatus: 'queued',
runVersion: 2,
eventSequence: 2,
executorType: 'remote_worker',
executionRevisionDigest: 'f'.repeat(64),
createdAtMs: 20,
};
},
},
taskDefinitions: {
async findCurrentTaskDefinition(projectId, taskId) {
events.push(`task-get:${projectId}:${taskId}`);
return projectId === currentTask.projectId && taskId === currentTask.taskId
? currentTask
: null;
},
async listTaskDefinitions(query) {
events.push(`task-list:${query.projectId}:${query.limit}`);
return {
definitions: [currentTask],
truncated: false,
};
},
},
taskExecutionRevisions: {},
triggers: {},
schedules: {},
securityAudit: {
record(record) {
events.push(`audit:${record.operationId}:${record.outcome}`);
},
},
workflowAdministration: {
async inspect(projectId, packageName) {
events.push(`workflow-read:${projectId}:${packageName}`);
return { found: false, publicationState: null, workflows: [] };
},
async inspectRun(command) {
events.push(
`workflow-run-read:${command.packageName}:${command.workflowId}:${command.runId}`,
);
return {
schema: 'qinglong/plugin-package-workflow-run-inspection@v1',
found: false,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
run: null,
stepCount: null,
stepStatusCounts: null,
};
},
async listRuns(command) {
events.push(
`workflow-run-list:${command.packageName}:${command.workflowId}:${command.limit}`,
);
return {
schema: 'qinglong/plugin-package-workflow-run-list@v1',
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
after: command.after,
runs: [],
truncated: false,
next: null,
};
},
async listStepRuns(command) {
events.push(
`workflow-step-list:${command.packageName}:${command.workflowId}:${command.runId}:${command.limit}`,
);
return {
schema: 'qinglong/plugin-package-workflow-step-run-list@v1',
found: false,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
stepRuns: [],
truncated: false,
next: null,
};
},
async listRunEvents(command) {
events.push(
`workflow-event-list:${command.packageName}:${command.workflowId}:${command.runId}:${command.limit}:${command.afterSequence}`,
);
return {
schema: 'qinglong/plugin-package-workflow-run-event-list@v1',
found: false,
projectId: command.projectId,
packageName: command.packageName,
workflowId: command.workflowId,
runId: command.runId,
afterSequence: command.afterSequence,
headSequence: null,
events: [],
truncated: false,
nextAfterSequence: null,
};
},
async start(command) {
events.push(`workflow-start:${command.workflowId}`);
return {
status: 'created',
plan: { planId: command.planId, runId: command.runId },
receipt: { receiptDigest: 'f'.repeat(64) },
};
},
async cancel(command) {
events.push(
`workflow-cancel:${command.packageName}:${command.workflowId}:${command.runId}:${command.eventId}`,
);
return {
status: 'accepted',
projectId: command.projectId,
runId: command.runId,
runStatus: 'running',
runVersion: 4,
eventSequence: 4,
cancelRequestedAtMs: 20,
cancelReason: 'user',
};
},
},
...overrides,
};
return { events, input };
}
async function invoke(stack, request) {
const prepared = await stack.admission.prepare(request);
return prepared.handle(request.body);
}
test('production composition exposes the reviewed Run and Workflow routes', async () => {
const { events, input } = fixture();
const stack = createProductionClusterControlApplicationStack(input, {
createEventId: () => EVENT_ID,
});
assert.deepEqual(PRODUCTION_CLUSTER_CONTROL_ROUTE_OPERATIONS, [
'task.get',
'task.list',
'task.start',
'run.get',
'run.list',
'run.events.list',
'run.steps.list',
'run.cancel',
'workflow.read',
'workflow.run.read',
'workflow.run.list',
'workflow.step.list',
'workflow.event.list',
'workflow.start',
'workflow.cancel',
]);
assert.deepEqual(await stack.reconcile(), {
safe: true,
remaining: 0,
failed: 0,
});
assert.equal(await stack.startLifecycles(), true);
const tasks = await invoke(
stack,
metadata('/api/v3/projects/project-1/tasks', 'GET', null, {
limit: ['8'],
}),
);
assert.equal(tasks.statusCode, 200);
assert.equal(tasks.body.tasks[0].taskId, 'task-1');
assert.equal(JSON.stringify(tasks).includes('private'), false);
const task = await invoke(
stack,
metadata('/api/v3/projects/project-1/tasks/task-1'),
);
assert.equal(task.statusCode, 200);
assert.equal(task.body.task.taskId, 'task-1');
assert.match(task.body.task.contentDigest, /^[0-9a-f]{64}$/);
assert.equal(JSON.stringify(task).includes('private'), false);
const read = await invoke(
stack,
metadata('/api/v3/projects/project-1/runs/run-1'),
);
assert.equal(read.statusCode, 200);
assert.equal(read.body.run.id, 'run-1');
const listed = await invoke(
stack,
metadata('/api/v3/projects/project-1/runs', 'GET', null, {
limit: ['8'],
}),
);
assert.equal(listed.statusCode, 200);
assert.equal(listed.body.runs[0].id, 'run-1');
const timeline = await invoke(
stack,
metadata('/api/v3/projects/project-1/runs/run-1/events', 'GET', null, {
after_sequence: ['0'],
limit: ['8'],
}),
);
assert.equal(timeline.statusCode, 200);
assert.equal(timeline.body.events[0].type, 'run.created');
assert.equal(JSON.stringify(timeline).includes('secret'), false);
const steps = await invoke(
stack,
metadata('/api/v3/projects/project-1/runs/run-1/steps', 'GET', null, {
limit: ['8'],
}),
);
assert.deepEqual(steps, {
statusCode: 200,
body: { steps: [], hasMore: false, next: null },
});
const cancellation = await invoke(
stack,
metadata('/api/v3/projects/project-1/runs/run-1/cancellation', 'POST', {
schema: 'qinglong/run-cancellation@v1',
mutationId: 'mutation-production-1',
}),
);
assert.equal(cancellation.statusCode, 202);
assert.equal(cancellation.body.status, 'accepted');
assert.equal(events.includes(`cancel:run-1:${EVENT_ID}`), true);
assert.equal(events.includes('audit:run.get:allowed'), true);
assert.equal(events.includes('audit:run.events.list:allowed'), true);
assert.equal(events.includes('audit:run.steps.list:allowed'), true);
assert.equal(events.includes('audit:run.cancel:allowed'), true);
const workflows = await invoke(
stack,
metadata('/api/v3/projects/project-1/packages/example/workflows'),
);
assert.equal(workflows.statusCode, 200);
assert.equal(workflows.body.found, false);
const workflowStart = await invoke(
stack,
metadata(
'/api/v3/projects/project-1/packages/example/workflows/daily/runs',
'POST',
{
schema: 'qinglong/cluster-plugin-package-workflow-start-request@v1',
planId: '123e4567-e89b-42d3-a456-426614174000',
runId: '123e4567-e89b-42d3-a456-426614174001',
stepRunIds: {
run: '123e4567-e89b-42d3-a456-426614174002',
},
},
),
);
assert.equal(workflowStart.statusCode, 201);
assert.equal(workflowStart.body.replayed, false);
assert.equal(events.includes('audit:workflow.read:allowed'), true);
assert.equal(events.includes('audit:workflow.start:allowed'), true);
const workflowRunRead = await invoke(
stack,
metadata(
'/api/v3/projects/project-1/packages/example/workflows/daily/runs/123e4567-e89b-42d3-a456-426614174001',
),
);
assert.equal(workflowRunRead.statusCode, 404);
assert.deepEqual(workflowRunRead.body, { code: 'workflow_run_not_found' });
assert.equal(
events.includes(
'workflow-run-read:example:daily:123e4567-e89b-42d3-a456-426614174001',
),
true,
);
assert.equal(events.includes('audit:workflow.run.read:allowed'), true);
const workflowRunListRequest = metadata(
'/api/v3/projects/project-1/packages/example/workflows/daily/runs',
);
const workflowRunList = await invoke(stack, {
...workflowRunListRequest,
query: { limit: ['16'] },
});
assert.equal(workflowRunList.statusCode, 200);
assert.deepEqual(workflowRunList.body.runs, []);
assert.equal(events.includes('workflow-run-list:example:daily:16'), true);
assert.equal(events.includes('audit:workflow.run.list:allowed'), true);
const workflowStepListRequest = metadata(
'/api/v3/projects/project-1/packages/example/workflows/daily/runs/123e4567-e89b-42d3-a456-426614174001/steps',
);
const workflowStepList = await invoke(stack, {
...workflowStepListRequest,
query: { limit: ['16'] },
});
assert.equal(workflowStepList.statusCode, 404);
assert.deepEqual(workflowStepList.body, { code: 'workflow_run_not_found' });
assert.equal(
events.includes(
'workflow-step-list:example:daily:123e4567-e89b-42d3-a456-426614174001:16',
),
true,
);
assert.equal(events.includes('audit:workflow.step.list:allowed'), true);
const workflowEventListRequest = metadata(
'/api/v3/projects/project-1/packages/example/workflows/daily/runs/123e4567-e89b-42d3-a456-426614174001/events',
);
const workflowEventList = await invoke(stack, {
...workflowEventListRequest,
query: { limit: ['16'], after_sequence: ['2'] },
});
assert.equal(workflowEventList.statusCode, 404);
assert.deepEqual(workflowEventList.body, { code: 'workflow_run_not_found' });
assert.equal(
events.includes(
'workflow-event-list:example:daily:123e4567-e89b-42d3-a456-426614174001:16:2',
),
true,
);
assert.equal(events.includes('audit:workflow.event.list:allowed'), true);
const workflowCancellation = await invoke(
stack,
metadata(
'/api/v3/projects/project-1/packages/example/workflows/daily/runs/123e4567-e89b-42d3-a456-426614174001/cancellation',
'POST',
{
schema: 'qinglong/run-cancellation@v1',
mutationId: 'workflow-cancel-production-1',
},
),
);
assert.equal(workflowCancellation.statusCode, 202);
assert.equal(workflowCancellation.body.status, 'accepted');
assert.equal(
events.includes(
`workflow-cancel:example:daily:123e4567-e89b-42d3-a456-426614174001:${EVENT_ID}`,
),
true,
);
assert.equal(events.includes('audit:workflow.cancel:allowed'), true);
assert.equal(await stack.stop(), 'stopped');
});
test('production composition fails closed for an unreviewed route', async () => {
const { input } = fixture();
const stack = createProductionClusterControlApplicationStack(input);
await assert.rejects(
stack.admission.prepare(
metadata('/api/v3/projects/project-1/runs/run-1/retry', 'POST'),
),
(error) => error?.statusCode === 404 && error?.code === 'route_not_found',
);
});
test('optionally exposes Prompt execution behind shared admission and policy', async () => {
const { events, input } = fixture();
let command;
const stack = createProductionClusterControlApplicationStack(input, {
promptExecution: {
now: () => 2_000,
maxExecutionMs: 10_000,
capability: {
async execute(value) {
command = value;
return {
status: 'executed',
admission: {
requestId: value.requestId,
invocationId: 'ppi:1',
runId: 'ppr:1',
stepRunId: 'pps:1',
},
finalization: { runStatus: 'succeeded' },
result: { text: 'live output' },
};
},
},
},
});
assert.deepEqual(PRODUCTION_CLUSTER_CONTROL_OPTIONAL_ROUTE_OPERATIONS, [
'prompt.read',
'prompt.execute',
'prompt.execution.read',
'prompt.execution.output.read',
'prompt.output.read',
]);
const response = await invoke(
stack,
metadata(
'/api/v3/projects/project-1/packages/example/prompts/summary/executions',
'POST',
{
schema: 'qinglong/cluster-plugin-package-prompt-execution-request@v2',
requestId: 'prompt-request-1',
traceId: 'trace-1',
parameters: { subject: 'private input' },
provider: 'openai-compatible',
model: 'model-a',
maxOutputTokens: 256,
timeoutMs: 5_000,
},
),
);
assert.equal(response.statusCode, 200);
assert.equal(command.projectId, 'project-1');
assert.deepEqual(command.policyFence, {
projectVersion: 3,
bindingVersion: 7,
});
assert.equal(events.includes('audit:prompt.execute:allowed'), true);
});
test('optionally exposes the redacted Prompt catalog behind shared admission and policy', async () => {
const { events, input } = fixture();
const stack = createProductionClusterControlApplicationStack(input, {
promptCatalog: {
capability: {
async inspect(projectId, packageName) {
return {
schema: 'qinglong/plugin-package-prompt-catalog@v1',
projectId,
packageName,
found: true,
publicationState: 'active',
prompts: [
{
id: 'summary',
name: 'Summary',
description: null,
parameters: [],
},
],
};
},
},
},
});
const result = await invoke(
stack,
metadata('/api/v3/projects/project-1/packages/example/prompts'),
);
assert.equal(result.statusCode, 200);
assert.equal(result.body.prompts[0].id, 'summary');
assert.equal(JSON.stringify(result).includes('template'), false);
assert.equal(events.includes('audit:prompt.read:allowed'), true);
});
test('optionally inspects one Prompt execution behind shared admission and policy', async () => {
const { events, input } = fixture();
let command;
const stack = createProductionClusterControlApplicationStack(input, {
createEventId: () => '00000000-0000-4000-8000-000000000011',
promptExecutionInspection: {
now: () => 2_000,
capability: {
async inspectAuthorized(value) {
command = value;
return {
schema: 'qinglong/plugin-package-prompt-execution-inspection@v1',
found: true,
projectId: value.projectId,
packageName: value.packageName,
promptId: value.promptId,
executionRequestId: value.executionRequestId,
execution: {
invocationId: 'invocation-1',
runId: '00000000-0000-4000-8000-000000000010',
stepRunId: 'step-1',
runStatus: 'running',
runVersion: 2,
eventSequence: 2,
stepStatus: 'running',
stepVersion: 2,
admittedAtMs: 1_000,
startedAtMs: 1_000,
finishedAtMs: null,
finalizedAtMs: null,
},
};
},
},
},
});
const result = await invoke(
stack,
metadata(
'/api/v3/projects/project-1/packages/example/prompts/summary/executions/execution-request-1',
),
);
assert.equal(result.statusCode, 200);
assert.equal(command.executionRequestId, 'execution-request-1');
assert.equal(command.audit.operationId, 'prompt.execution.read');
assert.equal(events.includes('audit:prompt.execution.read:allowed'), true);
});
test('optionally exposes Prompt output read behind shared admission and policy', async () => {
const { events, input } = fixture();
let command;
const stack = createProductionClusterControlApplicationStack(input, {
promptOutputRead: {
capability: {
async read(value) {
command = value;
return {
schema: 'qinglong/plugin-package-prompt-output-read-result@v1',
status: 'not_found',
};
},
},
},
});
const request = metadata(
'/api/v3/projects/project-1/runs/run-1/prompt-output-artifacts/pao:1',
);
const result = await invoke(stack, {
...request,
query: { artifact_digest: ['a'.repeat(64)] },
});
assert.equal(result.statusCode, 404);
assert.equal(command.projectId, 'project-1');
assert.equal(command.runId, 'run-1');
assert.equal(command.artifactId, 'pao:1');
assert.equal(command.principal.subject.id, 'app-production');
assert.equal(events.includes('audit:prompt.output.read:allowed'), true);
});
test('optionally recovers Prompt output by execution requestId behind artifact.read', async () => {
const { events, input } = fixture();
let command;
const stack = createProductionClusterControlApplicationStack(input, {
promptExecutionOutputRead: {
capability: {
async read(value) {
command = value;
return {
schema:
'qinglong/plugin-package-prompt-execution-output-read-result@v1',
status: 'not_found',
projectId: value.projectId,
packageName: value.packageName,
promptId: value.promptId,
executionRequestId: value.executionRequestId,
};
},
},
},
});
const result = await invoke(
stack,
metadata(
'/api/v3/projects/project-1/packages/example/prompts/summary/executions/execution-request-1/output',
),
);
assert.equal(result.statusCode, 404);
assert.equal(command.projectId, 'project-1');
assert.equal(command.packageName, 'example');
assert.equal(command.promptId, 'summary');
assert.equal(command.executionRequestId, 'execution-request-1');
assert.equal(command.principal.subject.id, 'app-production');
assert.equal(
events.includes('audit:prompt.execution.output.read:allowed'),
true,
);
});
test('production start requires an enabled configuration before owning resources', () => {
assert.throws(
() =>
startProductionClusterControlApplication({
config: { enabled: false, profile: 'standalone' },
recovery: { ownerId: 'production-test' },
audit() {},
}),
/database binding requires an enabled cluster-control config/,
);
});
test('production composition rejects an invalid event ID factory', () => {
const { input } = fixture();
assert.throws(
() =>
createProductionClusterControlApplicationStack(input, {
createEventId: 'invalid',
}),
/event ID factory is invalid/,
);
});
test('starts and stops Worker ingress through the injected runtime port', async () => {
const events = [];
const runtime = Object.freeze({
offers: { claimNext() {} },
activation: {
acknowledgeStarting() {},
acknowledgeRunning() {},
failStart() {},
},
artifacts: { upload() {} },
completion: { complete() {} },
leaseControl: { control() {} },
});
const { input } = fixture({ workerRuntime: runtime });
const config = {
enabled: true,
profile: 'cluster-control',
http: { host: '127.0.0.1', port: 5801 },
transport: {},
database: {},
security: { workerCredentialPepper: 'A'.repeat(43) },
artifact: {},
};
const stack = createProductionClusterControlApplicationStack(input, {
workerIngress: { config },
async startWorkerIngress(options) {
events.push('start-worker-ingress');
assert.equal(options.config, config);
assert.equal(options.runtime, runtime);
return {
status: 'active',
protocol: 'https',
transport: 'mutual-tls',
address: { host: '127.0.0.1', port: 5801 },
evidence: input.evidence,
reloadTransport() {
return 1;
},
async stop() {
events.push('stop-worker-ingress');
return 'stopped';
},
};
},
});
assert.equal(await stack.startLifecycles(), true);
assert.equal(await stack.startLifecycles(), true);
assert.equal(await stack.stop(), 'stopped');
assert.equal(await stack.stop(), 'stopped');
assert.deepEqual(events, ['start-worker-ingress', 'stop-worker-ingress']);
});
test('fails closed when Worker ingress has no runtime service port', async () => {
const { input } = fixture();
const stack = createProductionClusterControlApplicationStack(input, {
workerIngress: {
config: {
enabled: true,
profile: 'cluster-control',
},
},
async startWorkerIngress() {
throw new Error('must not start');
},
});
await assert.rejects(
stack.startLifecycles(),
/requires an injected runtime service port/,
);
});
test('drains a Worker listener when its Pool fails during activation', async () => {
let stops = 0;
const diagnostics = [];
const { input } = fixture({
workerRuntime: {
offers: { claimNext() {} },
activation: {
acknowledgeStarting() {},
acknowledgeRunning() {},
failStart() {},
},
artifacts: { upload() {} },
completion: { complete() {} },
leaseControl: { control() {} },
},
});
const stack = createProductionClusterControlApplicationStack(input, {
workerIngress: {
config: { enabled: true, profile: 'cluster-control' },
onDiagnostic(error) {
diagnostics.push(error);
},
},
async startWorkerIngress(options) {
options.onPoolError(new Error('worker database unavailable'));
return {
status: 'active',
protocol: 'https',
transport: 'mutual-tls',
address: { host: '127.0.0.1', port: 5801 },
evidence: input.evidence,
reloadTransport() {
return 1;
},
async stop() {
stops += 1;
return 'stopped';
},
};
},
});
await assert.rejects(
stack.startLifecycles(),
/became unavailable during activation/,
);
assert.equal(stops, 1);
assert.equal(diagnostics.length, 1);
});
@@ -0,0 +1,76 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterRemoteRunActivationService,
} = require('@qinglong/cluster-control/remote-activation');
const SESSION_ID = '018f5c64-9b9d-7f1a-8c2d-1234567890ac';
const EVENT_IDS = [
'018f5c64-9b9d-7f1a-8c2d-1234567890a1',
'018f5c64-9b9d-7f1a-8c2d-1234567890a2',
'018f5c64-9b9d-7f1a-8c2d-1234567890a3',
'018f5c64-9b9d-7f1a-8c2d-1234567890a4',
'018f5c64-9b9d-7f1a-8c2d-1234567890a5',
];
function command() {
return {
runId: 'run-1',
attemptId: 'attempt-1',
workerSessionId: SESSION_ID,
workerGeneration: 2,
offerId: 'offer-1',
leaseGeneration: 3,
leaseToken: 'worker_generated_lease_capability_0000000000000001',
expectedLeaseVersion: 4,
};
}
test('binds Worker principal and creates server-owned event IDs', async () => {
const observed = [];
const repository = {
async acknowledgeStarting(value) {
observed.push(['starting', value]);
return { status: 'applied', snapshot: {} };
},
async acknowledgeRunning(value) {
observed.push(['running', value]);
return { status: 'applied', snapshot: {} };
},
async failStart(value) {
observed.push(['failed', value]);
return { status: 'applied', snapshot: {} };
},
};
let sequence = 0;
const service = new ClusterRemoteRunActivationService(repository, {
createEventId: () => EVENT_IDS[sequence++],
});
const principal = { workerId: 'edge-1' };
await service.acknowledgeStarting(principal, command());
await service.acknowledgeRunning(principal, {
...command(),
executorHandle: 'remote:handle-1',
callbackSequence: 1,
callbackTokenDigest: 'a'.repeat(64),
});
await service.failStart(principal, command());
assert.deepEqual(observed, [
['starting', { ...command(), workerId: 'edge-1', eventId: EVENT_IDS[0] }],
['running', {
...command(),
executorHandle: 'remote:handle-1',
callbackSequence: 1,
callbackTokenDigest: 'a'.repeat(64),
workerId: 'edge-1',
attemptEventId: EVENT_IDS[1],
runEventId: EVENT_IDS[2],
}],
['failed', {
...command(),
workerId: 'edge-1',
attemptEventId: EVENT_IDS[3],
runEventId: EVENT_IDS[4],
}],
]);
});
@@ -0,0 +1,258 @@
'use strict';
const assert = require('node:assert/strict');
const { createHash } = require('node:crypto');
const { test } = require('node:test');
const {
RemoteWorkerCompletionFenceRejectedError,
RemoteWorkerCompletionUnavailableError,
createRemoteWorkerArtifactUploadPreamble,
} = require('@qinglong/runtime-core/remote-worker-completion');
const {
ClusterRemoteWorkerArtifactService,
ClusterRemoteWorkerCompletionService,
} = require('@qinglong/cluster-control/remote-completion');
const SESSION_ID = '018f0000-0000-7000-8000-000000000001';
const LEASE_TOKEN = 'worker_generated_lease_capability_0000000000000001';
const LOG_ARTIFACT_ID = `wlog-${'a'.repeat(30)}`;
const CALLBACK_TOKEN_DIGEST = 'b'.repeat(64);
function fence() {
return {
workerId: 'worker-1',
workerSessionId: SESSION_ID,
workerGeneration: 2,
projectId: 'project-1',
runId: 'run-1',
attemptId: 'attempt-1',
offerId: 'offer-1',
leaseGeneration: 3,
leaseToken: LEASE_TOKEN,
expectedLeaseVersion: 4,
};
}
function uploadCommand(content, overrides = {}) {
return {
...fence(),
logArtifactId: LOG_ARTIFACT_ID,
byteLength: content.byteLength,
truncated: false,
...overrides,
};
}
function completionCommand(content, overrides = {}) {
return {
...fence(),
callbackSequence: 1,
callbackTokenDigest: CALLBACK_TOKEN_DIGEST,
result: {
outcome: 'succeeded',
startedAtMs: 100,
finishedAtMs: 200,
exitCode: 0,
},
artifact: {
logArtifactId: LOG_ARTIFACT_ID,
byteLength: content.byteLength,
sha256: createHash('sha256').update(content).digest('hex'),
truncated: false,
},
...overrides,
};
}
function split(bytes, sizes) {
return (async function* () {
let offset = 0;
for (const size of sizes) {
yield bytes.subarray(offset, offset + size);
offset += size;
}
if (offset < bytes.byteLength) yield bytes.subarray(offset);
})();
}
test('authorizes a framed upload before storing exact capability-free bytes', async () => {
const content = Buffer.from('worker-log');
const command = uploadCommand(content);
const preamble = createRemoteWorkerArtifactUploadPreamble(command);
const envelope = Buffer.concat([preamble, content]);
const observations = [];
const service = new ClusterRemoteWorkerArtifactService(
{
async authorizeArtifactUpload(value) {
observations.push({ kind: 'authority', value });
},
},
{
async put(target, chunks) {
const stored = [];
for await (const chunk of chunks) stored.push(Buffer.from(chunk));
const bytes = Buffer.concat(stored);
observations.push({ kind: 'store', target, bytes });
return {
status: 'stored',
...target,
sha256: createHash('sha256').update(bytes).digest('hex'),
};
},
async inspect() { return undefined; },
},
);
const receipt = await service.upload({
workerId: command.workerId,
workerSessionId: command.workerSessionId,
contentLength: envelope.byteLength,
chunks: split(envelope, [1, 2, 1, 3, 5]),
});
assert.equal(receipt.sha256, createHash('sha256').update(content).digest('hex'));
assert.deepEqual(observations.map(({ kind }) => kind), ['authority', 'store']);
assert.deepEqual(observations[0].value, command);
assert.equal('leaseToken' in observations[1].target, false);
assert.equal('workerId' in observations[1].target, false);
assert.deepEqual(observations[1].bytes, content);
});
test('rejects envelope drift before authority or storage access', async () => {
const content = Buffer.from('log');
const command = uploadCommand(content);
const preamble = createRemoteWorkerArtifactUploadPreamble(command);
const envelope = Buffer.concat([preamble, content]);
let authorityCalls = 0;
let storeCalls = 0;
const service = new ClusterRemoteWorkerArtifactService(
{
async authorizeArtifactUpload() { authorityCalls += 1; },
},
{
async put() { storeCalls += 1; throw new Error('must not store'); },
async inspect() { return undefined; },
},
);
await assert.rejects(
service.upload({
workerId: command.workerId,
workerSessionId: command.workerSessionId,
contentLength: envelope.byteLength + 1,
chunks: split(envelope, [4]),
}),
/envelope length does not match/,
);
assert.equal(authorityCalls, 0);
assert.equal(storeCalls, 0);
});
test('fails closed when a store does not consume or drifts from the command', async () => {
const content = Buffer.from('log');
const command = uploadCommand(content);
const preamble = createRemoteWorkerArtifactUploadPreamble(command);
const envelope = Buffer.concat([preamble, content]);
const service = new ClusterRemoteWorkerArtifactService(
{ async authorizeArtifactUpload() {} },
{
async put(target) {
return { status: 'stored', ...target, sha256: 'c'.repeat(64) };
},
async inspect() { return undefined; },
},
);
await assert.rejects(
service.upload({
workerId: command.workerId,
workerSessionId: command.workerSessionId,
contentLength: envelope.byteLength,
chunks: split(envelope, [4]),
}),
RemoteWorkerCompletionUnavailableError,
);
});
test('inspects immutable Artifact evidence before one server-ID completion', async () => {
const content = Buffer.from('worker-log');
const command = completionCommand(content);
const calls = [];
const ids = [
'018f0000-0000-7000-8000-000000000011',
'018f0000-0000-7000-8000-000000000012',
];
const service = new ClusterRemoteWorkerCompletionService(
{
async complete(value) {
calls.push({ kind: 'repository', value });
return {
status: 'applied',
runId: value.runId,
attemptId: value.attemptId,
callbackSequence: value.callbackSequence,
};
},
},
{
async inspect(lookup) {
calls.push({ kind: 'store', lookup });
return {
status: 'already_stored',
...lookup,
byteLength: content.byteLength,
sha256: command.artifact.sha256,
truncated: false,
};
},
},
{ createEventId: () => ids.shift() },
);
assert.deepEqual(await service.complete(command), {
status: 'applied',
runId: 'run-1',
attemptId: 'attempt-1',
callbackSequence: 1,
});
assert.deepEqual(calls.map(({ kind }) => kind), ['store', 'repository']);
assert.equal(calls[1].value.attemptEventId.endsWith('11'), true);
assert.equal(calls[1].value.runEventId.endsWith('12'), true);
});
test('fences missing or digest-drifted Artifact evidence before completion', async () => {
const content = Buffer.from('worker-log');
const command = completionCommand(content);
let repositoryCalls = 0;
const repository = {
async complete() { repositoryCalls += 1; throw new Error('must not run'); },
};
const missing = new ClusterRemoteWorkerCompletionService(
repository,
{ async inspect() { return undefined; } },
);
await assert.rejects(
missing.complete(command),
(error) =>
error instanceof RemoteWorkerCompletionFenceRejectedError &&
error.reason === 'state_mismatch',
);
const drifted = new ClusterRemoteWorkerCompletionService(
repository,
{
async inspect(lookup) {
return {
status: 'stored',
...lookup,
byteLength: content.byteLength,
sha256: 'd'.repeat(64),
truncated: false,
};
},
},
);
await assert.rejects(
drifted.complete(command),
(error) =>
error instanceof RemoteWorkerCompletionFenceRejectedError &&
error.reason === 'replay_mismatch',
);
assert.equal(repositoryCalls, 0);
});
@@ -0,0 +1,139 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
canonicalRemoteWorkerCapabilities,
} = require('@qinglong/runtime-core/remote-dispatch');
const {
createClusterTaskExecutionRevision,
} = require('@qinglong/runtime-core/cluster-execution-revision');
const {
digestRunDispatchLeaseToken,
} = require('@qinglong/runtime-core');
const {
ClusterRemoteWorkerOfferClaimService,
ClusterRemoteWorkerOfferFenceRejectedError,
} = require('../dist/remote-execution/remoteWorkerDispatcher');
const SESSION = '018f0000-0000-7000-8000-000000000001';
const TOKEN = 'worker_generated_lease_capability_0000000000000001';
const SOURCE_DIGEST = 'a'.repeat(64);
const TASK_REVISION = `qltd:v1:1:${SOURCE_DIGEST}`;
function candidate() {
return {
runId: 'run-1', attemptId: 'attempt-1', projectId: 'project-1',
taskId: 'task-1', taskRevision: TASK_REVISION, priority: 2,
queuedAtMs: 10, attemptCreatedAtMs: 11, attemptNumber: 1,
executorType: 'remote_worker',
};
}
function revision() {
return createClusterTaskExecutionRevision({
projectId: 'project-1', taskId: 'task-1', taskRevision: TASK_REVISION,
sourceRevision: 1, sourceContentDigest: SOURCE_DIGEST,
executorType: 'remote_worker', planSchema: 'qinglong/command-execution@v1',
command: { kind: 'argv', file: '/usr/bin/node', args: ['job.js'] },
environment: [],
placement: { required: { architectures: ['arm64'] } },
createdAtMs: 1,
});
}
function worker() {
const capabilities = canonicalRemoteWorkerCapabilities({
architecture: 'arm64', executors: ['remote-worker'],
});
return {
workerId: 'edge-1', sessionId: SESSION, generation: 2,
status: 'online', version: 1,
capabilitiesJson: capabilities.json, capabilitiesHash: capabilities.hash,
maxConcurrentRuns: 1, availableSlots: 1,
registeredAtMs: 1, lastHeartbeatAtMs: 10,
leaseExpiresAtMs: 60_000, updatedAtMs: 10,
};
}
function lease() {
return {
attemptId: 'attempt-1', runId: 'run-1', status: 'leased', version: 0,
leaseGeneration: 1, workerId: 'edge-1', workerSessionId: SESSION,
workerGeneration: 2, leaseTokenDigest: digestRunDispatchLeaseToken(TOKEN),
acquiredAtMs: 1000, renewedAtMs: 1000, expiresAtMs: 31_000,
updatedAtMs: 1000,
};
}
function service(overrides = {}) {
const calls = [];
const source = overrides.source ?? {
async findClusterDispatchRecovery() { return null; },
async listClusterDispatchCandidates() {
calls.push('candidates');
return { observedAtMs: 1000, candidates: [candidate()], truncated: false };
},
};
const value = new ClusterRemoteWorkerOfferClaimService(
source,
overrides.workers ?? { async findById() { calls.push('worker'); return worker(); } },
overrides.revisions ?? {
async resolveClusterTaskExecutionRevision() { calls.push('revision'); return revision(); },
},
overrides.leases ?? {
async claim(command) { calls.push(`claim:${command.offerId}`); return { status: 'claimed', lease: lease() }; },
},
{ createEventId: () => 'event-1' },
);
return { value, calls };
}
const command = {
workerSessionId: SESSION,
workerGeneration: 2,
offerId: 'offer-1',
leaseToken: TOKEN,
};
test('pulls one bounded candidate, applies Placement and atomically returns a fenced offer', async () => {
const { value, calls } = service();
const result = await value.claimNext({ workerId: 'edge-1' }, command);
assert.equal(result.status, 'offered');
assert.equal(result.offer.deliveryKind, 'new_claim');
assert.equal(result.offer.worker.sessionId, SESSION);
assert.equal(result.offer.executionRevision.placement.required.executors[0], 'remote-worker');
assert.deepEqual(calls, ['candidates', 'worker', 'revision', 'claim:offer-1']);
});
test('rebuilds a lost response only for the same Worker-provided offer capability', async () => {
const { value } = service({
source: {
async findClusterDispatchRecovery() {
return {
observedAtMs: 2000,
candidate: candidate(),
lease: lease(),
workerCurrent: true,
};
},
async listClusterDispatchCandidates() { throw new Error('must not list'); },
},
});
const result = await value.claimNext({ workerId: 'edge-1' }, command);
assert.equal(result.status, 'offered');
assert.equal(result.offer.deliveryKind, 'lease_recovery');
await assert.rejects(
value.claimNext({ workerId: 'edge-1' }, { ...command, leaseToken: `${TOKEN}x` }),
ClusterRemoteWorkerOfferFenceRejectedError,
);
});
test('returns low-cardinality idle evidence when the authenticated Worker does not match', async () => {
const { value } = service({
workers: { async findById() { return { ...worker(), sessionId: '018f0000-0000-7000-8000-000000000002' }; } },
});
const result = await value.claimNext({ workerId: 'edge-1' }, command);
assert.deepEqual(
{ status: result.status, reason: result.reason },
{ status: 'idle', reason: 'worker_unavailable' },
);
});
@@ -0,0 +1,93 @@
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterRemoteWorkerLeaseControlService,
} = require('@qinglong/cluster-control/lease-control');
const SESSION_ID = '018f0000-0000-7000-8000-000000000001';
const LEASE_TOKEN = 'worker_generated_lease_capability_0000000000000001';
function command() {
return {
workerId: 'worker-1',
workerSessionId: SESSION_ID,
workerGeneration: 2,
projectId: 'project-1',
runId: 'run-1',
attemptId: 'attempt-1',
offerId: 'offer-1',
leaseGeneration: 3,
leaseToken: LEASE_TOKEN,
expectedLeaseVersion: 4,
};
}
test('adds server-owned renewal duration and timeout Event authority', async () => {
let observed;
const service = new ClusterRemoteWorkerLeaseControlService({
async control(value) {
observed = value;
return {
status: 'renewed',
projectId: value.projectId,
runId: value.runId,
attemptId: value.attemptId,
offerId: value.offerId,
leaseGeneration: value.leaseGeneration,
leaseVersion: value.expectedLeaseVersion + 1,
renewedAtMs: 10_000,
expiresAtMs: 55_000,
};
},
}, {
leaseDurationMs: 45_000,
createEventId: () => '018f0000-0000-7000-8000-000000000011',
});
assert.deepEqual(await service.control(command()), {
status: 'renewed',
projectId: 'project-1',
runId: 'run-1',
attemptId: 'attempt-1',
offerId: 'offer-1',
leaseGeneration: 3,
leaseVersion: 5,
renewedAtMs: 10_000,
expiresAtMs: 55_000,
});
assert.deepEqual(observed, {
...command(),
leaseDurationMs: 45_000,
timeoutEventId: '018f0000-0000-7000-8000-000000000011',
});
});
test('fails closed before repository access for invalid server Event IDs', async () => {
let calls = 0;
const service = new ClusterRemoteWorkerLeaseControlService({
async control() { calls += 1; throw new Error('must not run'); },
}, { createEventId: () => '' });
await assert.rejects(service.control(command()), /unavailable/);
assert.equal(calls, 0);
});
test('rejects a repository response that drifts from the wire contract', async () => {
const service = new ClusterRemoteWorkerLeaseControlService({
async control(value) {
return {
status: 'renewed',
projectId: value.projectId,
runId: value.runId,
attemptId: value.attemptId,
offerId: value.offerId,
leaseGeneration: value.leaseGeneration,
leaseVersion: value.expectedLeaseVersion + 1,
renewedAtMs: 10_000,
expiresAtMs: 10_000,
};
},
});
await assert.rejects(service.control(command()), /invalid/);
});
@@ -0,0 +1,133 @@
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
RemoteWorkerSecretDeliveryFenceRejectedError,
} = require('@qinglong/runtime-core/remote-secret-delivery');
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
const {
ClusterRemoteWorkerSecretDeliveryService,
} = require('../dist/remote-execution/remoteWorkerSecretDeliveryService');
const SESSION_ID = '018f0000-0000-7000-8000-000000000001';
const SECRET_REF = createSecretRef({ projectId: 'project-1', name: 'token' });
const DIGEST = 'a'.repeat(64);
function command() {
return {
workerSessionId: SESSION_ID, workerGeneration: 2,
runId: 'run-1', attemptId: 'attempt-1', projectId: 'project-1',
taskId: 'task-1', taskRevision: 'revision-1', executionDigest: DIGEST,
offerId: 'offer-1', leaseGeneration: 3,
leaseToken: 'worker_generated_lease_capability_0000000000000001',
expectedLeaseVersion: 4, secretRefs: [SECRET_REF],
};
}
function authority(input) {
const { leaseToken: _leaseToken, expectedLeaseVersion, ...rest } = input;
return {
workerId: 'edge-1', ...rest, leaseVersion: expectedLeaseVersion,
};
}
test('resolves plaintext only after repository authority succeeds', async () => {
const events = [];
let authorized;
const service = new ClusterRemoteWorkerSecretDeliveryService({
async authorize(input) {
events.push('authorize');
authorized = input;
return authority(input);
},
}, {
async resolve(input) {
events.push('resolve');
assert.equal('leaseToken' in input, false);
return {
values: [{ secretRef: SECRET_REF, value: 'resolved-value' }],
dispose() { events.push('dispose'); },
};
},
});
const result = await service.deliver({ workerId: 'edge-1' }, command());
assert.equal(authorized.workerId, 'edge-1');
assert.deepEqual(result.values, [
{ secretRef: SECRET_REF, value: 'resolved-value' },
]);
assert.deepEqual(events, ['authorize', 'resolve']);
await result.dispose();
assert.deepEqual(events, ['authorize', 'resolve', 'dispose']);
});
test('never calls the plaintext provider for fenced or replayed authority', async () => {
let resolutions = 0;
const service = new ClusterRemoteWorkerSecretDeliveryService({
async authorize() {
throw new RemoteWorkerSecretDeliveryFenceRejectedError('authority_mismatch');
},
}, {
async resolve() { resolutions += 1; },
});
await assert.rejects(
service.deliver({ workerId: 'edge-1' }, command()),
/authority_mismatch/,
);
assert.equal(resolutions, 0);
});
test('never calls plaintext provider when an injected repository widens authority', async () => {
let resolutions = 0;
const service = new ClusterRemoteWorkerSecretDeliveryService({
async authorize(input) {
return { ...authority(input), taskId: 'task-other' };
},
}, {
async resolve() { resolutions += 1; },
});
await assert.rejects(
service.deliver({ workerId: 'edge-1' }, command()),
/unavailable/,
);
assert.equal(resolutions, 0);
});
test('disposes malformed provider output and converts it to unavailable', async () => {
let disposed = 0;
const service = new ClusterRemoteWorkerSecretDeliveryService({
async authorize(input) { return authority(input); },
}, {
async resolve() {
return {
values: [{ secretRef: SECRET_REF, value: 'x'.repeat(17 * 1024) }],
dispose() { disposed += 1; },
};
},
});
await assert.rejects(
service.deliver({ workerId: 'edge-1' }, command()),
/unavailable/,
);
assert.equal(disposed, 1);
});
test('rejects extensible provider output and still invokes valid cleanup', async () => {
let disposed = 0;
const service = new ClusterRemoteWorkerSecretDeliveryService({
async authorize(input) { return authority(input); },
}, {
async resolve() {
return {
values: [{ secretRef: SECRET_REF, value: 'resolved-value' }],
dispose() { disposed += 1; },
diagnostic: 'must-not-cross-boundary',
};
},
});
await assert.rejects(
service.deliver({ workerId: 'edge-1' }, command()),
/unavailable/,
);
assert.equal(disposed, 1);
});
@@ -0,0 +1,184 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS,
ClusterControlRouteRegistryConfigurationError,
ClusterControlRouteResolutionError,
createClusterControlRouteRegistry,
isClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
function metadata(overrides = {}) {
return Object.freeze({
requestId: 'request-1',
method: 'POST',
path: '/api/v3/projects/prj_default/runs',
query: Object.freeze({}),
headers: Object.freeze({}),
signal: new AbortController().signal,
...overrides,
});
}
function definition(overrides = {}) {
return {
method: 'POST',
path: '/api/v3/projects/{projectId}/runs',
operationId: 'run.create',
permission: 'run.start',
projectParameter: 'projectId',
handle(request, parameters) {
return {
statusCode: 202,
body: {
projectId: request.projectId,
parameter: parameters.projectId,
},
};
},
...overrides,
};
}
test('compiles one immutable reviewed route and owns its Project scope', async () => {
const source = definition({ allowedQuery: ['dry_run'] });
const registry = createClusterControlRouteRegistry([source]);
source.operationId = 'forged.operation';
assert.equal(isClusterControlRouteRegistry(registry), true);
assert.equal(isClusterControlRouteRegistry({ ...registry }), false);
assert.equal(Object.isFrozen(registry), true);
assert.equal(registry.contractVersion, 1);
assert.equal(registry.size, 1);
const route = registry.resolve(
metadata({ query: Object.freeze({ dry_run: Object.freeze(['true']) }) }),
);
assert.equal(route.operationId, 'run.create');
assert.equal(route.permission, 'run.start');
assert.equal(route.projectId, 'prj_default');
assert.equal(Object.isFrozen(route), true);
assert.deepEqual(
await route.handle({
request: { ...metadata(), body: { taskId: 'task-1' } },
principal: {
subject: { type: 'user', id: 'usr_primary' },
authenticationId: 'session:1',
authenticatedAtMs: 1,
expiresAtMs: 2,
assurance: 'multi_factor',
},
operationId: route.operationId,
permission: route.permission,
projectId: route.projectId,
policyFence: { projectVersion: 1, bindingVersion: 1 },
}),
{
statusCode: 202,
body: { projectId: 'prj_default', parameter: 'prj_default' },
},
);
});
test('rejects widened, ambiguous and unbounded route definitions at startup', () => {
const invalidSets = [
[definition({ extra: true })],
[definition({ path: '/api/v3/projects/{projectId}/' })],
[definition({ path: '/api/v3/projects/%7BprojectId%7D/runs' })],
[definition({ projectParameter: 'missing' })],
[definition(), definition({ operationId: 'run.create' })],
[
definition(),
definition({
path: '/api/v3/projects/fixed/runs',
operationId: 'run.create.fixed',
}),
],
];
for (const definitions of invalidSets) {
assert.throws(
() => createClusterControlRouteRegistry(definitions),
ClusterControlRouteRegistryConfigurationError,
);
}
const tooMany = Array.from(
{ length: CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxRoutes + 1 },
(_, index) =>
definition({
path: `/api/v3/routes/route-${index}`,
operationId: `route.operation:${index}`,
projectParameter: null,
}),
);
assert.throws(
() => createClusterControlRouteRegistry(tooMany),
ClusterControlRouteRegistryConfigurationError,
);
});
test('rejects non-canonical paths and unknown query before returning a route', () => {
const registry = createClusterControlRouteRegistry([
definition({ allowedQuery: ['dry_run'] }),
]);
for (const path of [
'/api/v3/projects/prj_default/runs/',
'/api/v3/projects//runs',
'/api/v3/projects/%2e%2e/runs',
'/api/v3/projects/prj_default\\runs',
]) {
assert.throws(
() => registry.resolve(metadata({ path })),
(error) =>
error instanceof ClusterControlRouteResolutionError &&
error.statusCode === 400 &&
error.code === 'invalid_route_path',
);
}
assert.throws(
() =>
registry.resolve(
metadata({ query: Object.freeze({ debug: Object.freeze(['1']) }) }),
),
(error) =>
error instanceof ClusterControlRouteResolutionError &&
error.code === 'invalid_route_query',
);
assert.equal(
registry.resolve(metadata({ method: 'GET' })),
null,
'method is part of the reviewed route identity',
);
assert.equal(
registry.resolve(metadata({ path: '/api/v3/projects/prj_default/tasks' })),
null,
);
});
test('bounds repeated query values and rejects control characters', () => {
const registry = createClusterControlRouteRegistry([
definition({ allowedQuery: ['cursor'] }),
]);
for (const values of [
[],
Array.from(
{
length:
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxQueryValuesPerParameter + 1,
},
() => 'value',
),
['unsafe\u0000value'],
['x'.repeat(CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxQueryValueBytes + 1)],
]) {
assert.throws(
() =>
registry.resolve(
metadata({ query: Object.freeze({ cursor: Object.freeze(values) }) }),
),
(error) =>
error instanceof ClusterControlRouteResolutionError &&
error.code === 'invalid_route_query',
);
}
});
@@ -0,0 +1,57 @@
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterRunCancellationConvergenceLifecycle,
} = require('../dist/run/runCancellationLifecycle');
function summary() {
return {
pages: 1,
scanned: 1,
settledRuns: 1,
settledAttempts: 0,
blocked: 0,
hasMore: false,
remaining: false,
stopReason: 'complete',
};
}
test('coalesces one bounded cycle and drains without owning per-Run timers', async () => {
let release;
let calls = 0;
const lifecycle = new ClusterRunCancellationConvergenceLifecycle({
async reconcile() {
calls += 1;
await new Promise((resolve) => { release = resolve; });
return summary();
},
}, { intervalMs: 10_000, stopTimeoutMs: 1_000 });
assert.equal(lifecycle.start(), 'started');
const first = lifecycle.runOnce();
const second = lifecycle.runOnce();
assert.equal(first, second);
while (!release) await new Promise((resolve) => setImmediate(resolve));
release();
assert.deepEqual(await first, summary());
assert.equal(calls, 1);
assert.deepEqual(await lifecycle.stopAndDrain(), { status: 'stopped' });
await assert.rejects(lifecycle.runOnce(), /stopping/);
});
test('reports a bounded drain timeout without cancelling database authority', async () => {
const lifecycle = new ClusterRunCancellationConvergenceLifecycle({
reconcile: () => new Promise(() => {}),
}, { intervalMs: 10_000, stopTimeoutMs: 100 });
lifecycle.start();
void lifecycle.runOnce();
assert.deepEqual(await lifecycle.stopAndDrain(), { status: 'timed_out' });
});
test('rejects an unbounded cadence configuration', () => {
assert.throws(() => new ClusterRunCancellationConvergenceLifecycle({
async reconcile() { return summary(); },
}, { intervalMs: 249, stopTimeoutMs: 1_000 }));
});
@@ -0,0 +1,206 @@
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_RUN_CANCELLATION_SCHEMA,
ClusterRunCancellationFenceRejectedError,
ClusterRunCancellationNotFoundError,
ClusterRunCancellationUnavailableError,
} = require('@qinglong/runtime-core/cluster-run-cancellation');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
const {
CLUSTER_CONTROL_RUN_CANCELLATION_ROUTE,
createClusterControlRunCancellationRoute,
} = require('@qinglong/cluster-control/run-routes');
const EVENT_ID = '018f0000-0000-7000-8000-000000000001';
const PRINCIPAL = Object.freeze({
subject: Object.freeze({ type: 'user', id: 'user-1' }),
authenticationId: 'session:user-1',
authenticatedAtMs: 9_000,
expiresAtMs: 11_000,
assurance: 'single_factor',
});
const METADATA = Object.freeze({
requestId: 'request-cancel-run',
method: 'POST',
path: '/api/v3/projects/project-1/runs/run-1/cancellation',
query: Object.freeze({}),
headers: Object.freeze({ authorization: 'Bearer opaque' }),
signal: new AbortController().signal,
});
function accepted(overrides = {}) {
return {
status: 'accepted',
projectId: 'project-1',
runId: 'run-1',
runStatus: 'running',
runVersion: 5,
eventSequence: 7,
cancelRequestedAtMs: 10_000,
cancelReason: 'user',
...overrides,
};
}
function pipeline(repository, events = [], policyFence = {
projectVersion: 2,
bindingVersion: 3,
}) {
return createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([
createClusterControlRunCancellationRoute(repository, () => EVENT_ID),
]),
authenticator: {
authenticate() {
events.push('authenticate');
return PRINCIPAL;
},
},
policy: {
authorize(request) {
events.push(`authorize:${request.permission}:${request.projectId}`);
return { effect: 'allow', reasons: ['role_grant'], fence: policyFence };
},
},
audit: {
record(record) {
events.push(`audit:${record.outcome}:${record.operationId}`);
},
},
now: () => 10_000,
});
}
test('publishes one reviewed run.stop mutation route', () => {
const route = createClusterControlRunCancellationRoute({
async requestUserCancellation() { return accepted(); },
}, () => EVENT_ID);
assert.deepEqual(CLUSTER_CONTROL_RUN_CANCELLATION_ROUTE, {
method: 'POST',
path: '/api/v3/projects/{projectId}/runs/{runId}/cancellation',
operationId: 'run.cancel',
permission: 'run.stop',
projectParameter: 'projectId',
});
assert.equal(Object.isFrozen(route), true);
});
test('authenticates, authorizes and audits before committing cancellation', async () => {
const events = [];
let observed;
const prepared = await pipeline({
async requestUserCancellation(command) {
events.push('repository');
observed = command;
return accepted();
},
}, events).prepare(METADATA);
assert.deepEqual(events, [
'authenticate',
'authorize:run.stop:project-1',
'audit:allowed:run.cancel',
]);
assert.deepEqual(await prepared.handle({
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
mutationId: 'mutation-1',
}), {
statusCode: 202,
body: {
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
...accepted(),
},
});
assert.deepEqual(observed, {
projectId: 'project-1',
runId: 'run-1',
mutationId: 'mutation-1',
eventId: EVENT_ID,
subject: PRINCIPAL.subject,
policyFence: { projectVersion: 2, bindingVersion: 3 },
});
assert.equal(events.at(-1), 'repository');
});
test('rejects caller-selected reasons and missing policy fences', async () => {
let calls = 0;
const repository = {
async requestUserCancellation() { calls += 1; return accepted(); },
};
const prepared = await pipeline(repository).prepare(METADATA);
assert.deepEqual(await prepared.handle({
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
mutationId: 'mutation-1',
reason: 'shutdown',
}), {
statusCode: 400,
body: {
code: 'invalid_run_cancellation_request',
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
},
});
const unfenced = await pipeline(repository, [], null).prepare(METADATA);
assert.deepEqual(await unfenced.handle({
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
mutationId: 'mutation-1',
}), {
statusCode: 503,
body: { code: 'run_cancellation_unavailable' },
});
assert.equal(calls, 0);
});
test('maps replay, terminal, missing, fenced and unavailable outcomes', async () => {
for (const [outcome, expected] of [
[accepted({ status: 'already_requested' }), 200],
[{
status: 'already_terminal',
projectId: 'project-1',
runId: 'run-1',
runStatus: 'succeeded',
runVersion: 6,
eventSequence: 8,
}, 200],
]) {
const prepared = await pipeline({
async requestUserCancellation() { return outcome; },
}).prepare(METADATA);
const result = await prepared.handle({
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
mutationId: 'mutation-1',
});
assert.equal(result.statusCode, expected);
}
const errors = [
[new ClusterRunCancellationNotFoundError(), 404, 'run_not_found'],
[
new ClusterRunCancellationFenceRejectedError('authorization_changed'),
409,
'run_cancellation_fence_rejected',
],
[
new ClusterRunCancellationUnavailableError(),
503,
'run_cancellation_unavailable',
],
];
for (const [error, statusCode, code] of errors) {
const prepared = await pipeline({
async requestUserCancellation() { throw error; },
}).prepare(METADATA);
const result = await prepared.handle({
schema: CLUSTER_RUN_CANCELLATION_SCHEMA,
mutationId: 'mutation-1',
});
assert.equal(result.statusCode, statusCode);
assert.equal(result.body.code, code);
}
});
@@ -0,0 +1,146 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_CONTROL_RUN_EVENT_LIST_ROUTE,
createClusterControlRunEventListRoute,
} = require('@qinglong/cluster-control/run-routes');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
function run(projectId = 'prj_default') {
return { id: 'run-1', projectId };
}
function event(sequence, overrides = {}) {
return {
id: `event-${sequence}`,
runId: 'run-1',
sequence,
type: `run.event.${sequence}`,
actorType: 'system',
actorId: 'private-actor',
payload: { secret: 'must-not-cross-projection' },
createdAtMs: 1_000 + sequence,
...overrides,
};
}
function authorized(query = {}, body = null) {
return {
projectId: 'prj_default',
request: { query, body },
};
}
test('publishes one reviewed bounded Run event list route', () => {
assert.deepEqual(CLUSTER_CONTROL_RUN_EVENT_LIST_ROUTE, {
method: 'GET',
path: '/api/v3/projects/{projectId}/runs/{runId}/events',
operationId: 'run.events.list',
permission: 'run.read',
projectParameter: 'projectId',
allowedQuery: ['after_sequence', 'limit'],
});
assert.throws(() => createClusterControlRunEventListRoute({}), TypeError);
});
test('returns the shared projection with exact sequence keyset input', async () => {
const calls = [];
const route = createClusterControlRunEventListRoute({
async findRunById(runId) {
calls.push(['run', runId]);
return run();
},
async listEvents(runId, input) {
calls.push(['events', runId, input]);
return [event(3), event(4)];
},
});
const result = await route.handle(
authorized({ after_sequence: ['2'], limit: ['1'] }),
{ runId: 'run-1' },
);
assert.deepEqual(calls, [
['run', 'run-1'],
['events', 'run-1', { afterSequence: 2, limit: 2 }],
]);
assert.deepEqual(result, {
statusCode: 200,
body: {
events: [
{
sequence: 3,
type: 'run.event.3',
actorType: 'system',
createdAtMs: 1_003,
},
],
hasMore: true,
nextAfterSequence: 3,
},
});
assert.equal(JSON.stringify(result).includes('private'), false);
assert.equal(JSON.stringify(result).includes('secret'), false);
});
test('masks Project mismatch and rejects malformed query before authentication', async () => {
const route = createClusterControlRunEventListRoute({
async findRunById() {
return run('prj_other');
},
async listEvents() {
throw new Error('must not read');
},
});
assert.deepEqual(await route.handle(authorized(), { runId: 'run-1' }), {
statusCode: 404,
body: { code: 'run_not_found' },
});
let authentications = 0;
const pipeline = createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([route]),
authenticator: {
authenticate() {
authentications += 1;
return null;
},
},
policy: {
authorize() {
throw new Error('must not authorize');
},
},
audit: {
record() {
throw new Error('must not audit');
},
},
now: () => 10_000,
});
for (const query of [
{ after_sequence: ['02'] },
{ after_sequence: ['-1'] },
{ limit: ['65'] },
{ limit: ['1', '2'] },
]) {
await assert.rejects(
pipeline.prepare({
requestId: 'request-invalid-run-event-list',
method: 'GET',
path: '/api/v3/projects/prj_default/runs/run-1/events',
query,
headers: {},
signal: new AbortController().signal,
}),
(error) =>
error.statusCode === 400 && error.code === 'invalid_route_query',
);
}
assert.equal(authentications, 0);
});
@@ -0,0 +1,135 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_CONTROL_RUN_LIST_ROUTE,
createClusterControlRunListRoute,
} = require('@qinglong/cluster-control/run-routes');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
function run(id, createdAtMs, overrides = {}) {
return {
id,
projectId: 'prj_default',
taskId: `task-${id}`,
taskRevision: 'revision-1',
triggerType: 'manual',
executionOrigin: 'manual',
executionOwner: 'runtime',
status: 'running',
version: 0,
eventSequence: 1,
priority: 0,
createdAtMs,
...overrides,
};
}
function authorized(query = {}, body = null) {
return {
projectId: 'prj_default',
request: { query, body },
};
}
test('publishes one reviewed bounded Run list route', () => {
assert.deepEqual(CLUSTER_CONTROL_RUN_LIST_ROUTE, {
method: 'GET',
path: '/api/v3/projects/{projectId}/runs',
operationId: 'run.list',
permission: 'run.read',
projectParameter: 'projectId',
allowedQuery: ['after_created_at_ms', 'after_run_id', 'limit'],
});
assert.throws(() => createClusterControlRunListRoute({}), TypeError);
});
test('parses an exact keyset page and returns the shared projection', async () => {
const calls = [];
const route = createClusterControlRunListRoute({
async listRunsByProject(query) {
calls.push(query);
return [run('run-a', 90)];
},
});
const result = await route.handle(
authorized({
limit: ['8'],
after_created_at_ms: ['100'],
after_run_id: ['run-b'],
}),
{},
);
assert.equal(result.statusCode, 200);
assert.equal(result.body.runs[0].id, 'run-a');
assert.deepEqual(calls, [
{
projectId: 'prj_default',
limit: 9,
after: { createdAtMs: 100, runId: 'run-b' },
},
]);
});
test('rejects malformed query and body and masks repository failures', async () => {
const route = createClusterControlRunListRoute({
async listRunsByProject() { throw new Error('offline'); },
});
for (const query of [
{ limit: ['08'] },
{ limit: ['65'] },
{ limit: ['8', '9'] },
{ after_run_id: ['run-a'] },
{ after_created_at_ms: ['-1'], after_run_id: ['run-a'] },
]) {
assert.deepEqual(await route.handle(authorized(query), {}), {
statusCode: 400,
body: { code: 'invalid_run_list_query' },
});
}
assert.deepEqual(await route.handle(authorized({}, { value: true }), {}), {
statusCode: 400,
body: { code: 'invalid_request_body' },
});
assert.deepEqual(await route.handle(authorized({}), {}), {
statusCode: 503,
body: { code: 'run_list_unavailable' },
});
});
test('rejects non-canonical pagination before authentication', async () => {
let authentications = 0;
const pipeline = createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([
createClusterControlRunListRoute({
async listRunsByProject() { return []; },
}),
]),
authenticator: {
authenticate() {
authentications += 1;
return null;
},
},
policy: { authorize() { throw new Error('must not authorize'); } },
audit: { record() { throw new Error('must not audit'); } },
now: () => 10_000,
});
await assert.rejects(
pipeline.prepare({
requestId: 'request-invalid-run-list',
method: 'GET',
path: '/api/v3/projects/prj_default/runs',
query: { limit: ['08'] },
headers: {},
signal: new AbortController().signal,
}),
(error) => error.statusCode === 400 && error.code === 'invalid_route_query',
);
assert.equal(authentications, 0);
});
@@ -0,0 +1,300 @@
const assert = require('node:assert/strict');
const http = require('node:http');
const { test } = require('node:test');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
const {
CLUSTER_CONTROL_RUN_READ_ROUTE,
createClusterControlRunReadRoute,
} = require('@qinglong/cluster-control/run-routes');
const {
startClusterControlHttpSurface,
} = require('@qinglong/cluster-control/http');
const NOW = 10_000;
const PRINCIPAL = Object.freeze({
subject: Object.freeze({ type: 'user', id: 'usr_viewer' }),
authenticationId: 'session:viewer',
authenticatedAtMs: 9_000,
expiresAtMs: 11_000,
assurance: 'single_factor',
});
const METADATA = Object.freeze({
requestId: 'request-run-read',
method: 'GET',
path: '/api/v3/projects/prj_default/runs/run_123',
query: Object.freeze({}),
headers: Object.freeze({ authorization: 'Bearer opaque' }),
signal: new AbortController().signal,
});
const EVIDENCE = Object.freeze({
contractName: 'control-core',
contractVersion: 5,
serverMajor: 16,
migrationIds: Object.freeze(['pg-0001', 'pg-0002']),
});
function httpRequest(address, path) {
return new Promise((resolve, reject) => {
const outgoing = http.request(
{
host: address.host,
port: address.port,
path,
headers: {
authorization: 'Bearer opaque',
connection: 'close',
},
},
(response) => {
const chunks = [];
response.on('data', (chunk) => chunks.push(chunk));
response.on('end', () => {
resolve({
statusCode: response.statusCode,
body: JSON.parse(Buffer.concat(chunks).toString('utf8')),
});
});
},
);
outgoing.once('error', reject);
outgoing.end();
});
}
function run(overrides = {}) {
return {
id: 'run_123',
projectId: 'prj_default',
taskId: 'task_1',
taskRevision: 'revision_7',
taskName: 'must not cross the wire',
taskSnapshotRef: 'secret-adjacent-ref',
triggerType: 'manual',
executionOrigin: 'manual',
executionOwner: 'runtime',
triggeredBy: 'private-user-id',
requestId: 'private-request-id',
status: 'running',
version: 4,
eventSequence: 6,
priority: 10,
inputRef: 'private-input-ref',
outputRef: 'private-output-ref',
createdAtMs: 1_000,
queuedAtMs: 2_000,
startedAtMs: 3_000,
errorCode: 'private-error-code',
errorSummary: 'private error detail',
...overrides,
};
}
function admission(repository, events = [], overrides = {}) {
return createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([
createClusterControlRunReadRoute(repository),
]),
authenticator: {
authenticate() {
events.push('authenticate');
return PRINCIPAL;
},
},
policy: {
authorize(request) {
events.push(`authorize:${request.permission}:${request.projectId}`);
return {
effect: 'allow',
reasons: ['role_grant'],
fence: { projectVersion: 2, bindingVersion: 3 },
};
},
},
audit: {
record(record) {
events.push(`audit:${record.outcome}:${record.operationId}`);
},
},
now: () => NOW,
...overrides,
});
}
test('publishes one immutable reviewed Run read route', () => {
const route = createClusterControlRunReadRoute({
async findRunById() {
return null;
},
});
assert.deepEqual(CLUSTER_CONTROL_RUN_READ_ROUTE, {
method: 'GET',
path: '/api/v3/projects/{projectId}/runs/{runId}',
operationId: 'run.get',
permission: 'run.read',
projectParameter: 'projectId',
});
assert.equal(Object.isFrozen(route), true);
assert.throws(() => createClusterControlRunReadRoute({}), TypeError);
});
test('authenticates, authorizes and audits before one bounded Run lookup', async () => {
const events = [];
const pipeline = admission(
{
async findRunById(runId) {
events.push(`repository:${runId}`);
return run({ version: 0 });
},
},
events,
);
const prepared = await pipeline.prepare(METADATA);
assert.deepEqual(events, [
'authenticate',
'authorize:run.read:prj_default',
'audit:allowed:run.get',
]);
assert.deepEqual(await prepared.handle(null), {
statusCode: 200,
body: {
run: {
id: 'run_123',
projectId: 'prj_default',
taskId: 'task_1',
taskRevision: 'revision_7',
status: 'running',
version: 0,
eventSequence: 6,
priority: 10,
executionOrigin: 'manual',
executionOwner: 'runtime',
createdAtMs: 1_000,
queuedAtMs: 2_000,
startedAtMs: 3_000,
finishedAtMs: null,
},
},
});
assert.deepEqual(events.slice(-1), ['repository:run_123']);
assert.equal(
JSON.stringify(await prepared.handle(null)).includes('private'),
false,
);
});
test('serves the reviewed Run projection through the bounded HTTP surface', async (t) => {
const events = [];
const surface = await startClusterControlHttpSurface({
host: '127.0.0.1',
port: 0,
});
t.after(() => surface.close());
const dispose = surface.installAdmission(
EVIDENCE,
admission(
{
async findRunById(runId) {
events.push(`repository:${runId}`);
return run({ status: 'succeeded', finishedAtMs: 4_000 });
},
},
events,
),
);
t.after(() => dispose());
const result = await httpRequest(
surface.address,
'/api/v3/projects/prj_default/runs/run_123',
);
assert.equal(result.statusCode, 200);
assert.equal(result.body.run.id, 'run_123');
assert.equal(result.body.run.projectId, 'prj_default');
assert.equal(result.body.run.status, 'succeeded');
assert.equal(result.body.run.finishedAtMs, 4_000);
assert.equal(JSON.stringify(result).includes('private'), false);
assert.deepEqual(events, [
'authenticate',
'authorize:run.read:prj_default',
'audit:allowed:run.get',
'repository:run_123',
]);
});
test('does not query storage when authentication fails', async () => {
let queries = 0;
const pipeline = admission(
{
async findRunById() {
queries += 1;
return run();
},
},
[],
{ authenticator: { authenticate: () => null } },
);
await assert.rejects(
pipeline.prepare(METADATA),
(error) =>
error.statusCode === 401 && error.code === 'authentication_required',
);
assert.equal(queries, 0);
});
test('masks absent, cross-Project, corrupt and unavailable Run records', async () => {
for (const [record, expected] of [
[null, { statusCode: 404, body: { code: 'run_not_found' } }],
[
run({ projectId: 'prj_other' }),
{ statusCode: 404, body: { code: 'run_not_found' } },
],
[
run({ taskRevision: '\ncorrupt' }),
{ statusCode: 503, body: { code: 'run_query_unavailable' } },
],
[
{ id: 'run_123' },
{ statusCode: 503, body: { code: 'run_query_unavailable' } },
],
]) {
const prepared = await admission({
async findRunById() {
return record;
},
}).prepare(METADATA);
assert.deepEqual(await prepared.handle(null), expected);
}
const unavailable = await admission({
async findRunById() {
throw new Error('postgresql secret detail');
},
}).prepare(METADATA);
const response = await unavailable.handle(null);
assert.deepEqual(response, {
statusCode: 503,
body: { code: 'run_query_unavailable' },
});
assert.equal(JSON.stringify(response).includes('postgresql'), false);
});
test('rejects a GET body without touching the Run repository', async () => {
let queries = 0;
const prepared = await admission({
async findRunById() {
queries += 1;
return run();
},
}).prepare(METADATA);
assert.deepEqual(await prepared.handle({ unexpected: true }), {
statusCode: 400,
body: { code: 'invalid_request_body' },
});
assert.equal(queries, 0);
});
@@ -0,0 +1,156 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_CONTROL_RUN_STEP_LIST_ROUTE,
createClusterControlRunStepListRoute,
} = require('@qinglong/cluster-control/run-routes');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
const {
createStepRunRecord,
} = require('../../ql3-runtime-core/dist/run/stepRun.js');
function run(projectId = 'prj_default') {
return { id: 'run-1', projectId };
}
function step(id, stepKey) {
return createStepRunRecord({
id,
runId: 'run-1',
parentStepRunId: 'step-parent',
stepKey,
kind: 'tool',
definitionRef: 'tool:private.internal@1.0.0',
definitionDigest: 'a'.repeat(64),
required: true,
initialStatus: 'ready',
inputRef: 'artifact:private-input',
mutationId: `create-${id}`,
createdAtMs: 1_000,
});
}
function authorized(query = {}, body = null) {
return { projectId: 'prj_default', request: { query, body } };
}
test('publishes one reviewed bounded Run Step list route', () => {
assert.deepEqual(CLUSTER_CONTROL_RUN_STEP_LIST_ROUTE, {
method: 'GET',
path: '/api/v3/projects/{projectId}/runs/{runId}/steps',
operationId: 'run.steps.list',
permission: 'run.read',
projectParameter: 'projectId',
allowedQuery: ['after_step_key', 'after_step_run_id', 'limit'],
});
assert.throws(() => createClusterControlRunStepListRoute({}, {}), TypeError);
});
test('returns the shared projection with the paired Step keyset cursor', async () => {
const calls = [];
const first = step('step-1', 'build');
const second = step('step-2', 'deploy');
const route = createClusterControlRunStepListRoute(
{
async findRunById(runId) {
calls.push(['run', runId]);
return run();
},
},
{
async listByRun(query) {
calls.push(['steps', query]);
return {
stepRuns: [first, second],
truncated: true,
next: { stepKey: second.stepKey, id: second.id },
};
},
},
);
const result = await route.handle(
authorized({
after_step_key: ['admit'],
after_step_run_id: ['step-0'],
limit: ['2'],
}),
{ runId: 'run-1' },
);
assert.deepEqual(calls[1], [
'steps',
{
runId: 'run-1',
limit: 2,
after: { stepKey: 'admit', id: 'step-0' },
},
]);
assert.equal(result.statusCode, 200);
assert.equal(result.body.steps.length, 2);
assert.deepEqual(result.body.next, {
stepKey: 'deploy',
stepRunId: 'step-2',
});
assert.equal(JSON.stringify(result).includes('private'), false);
});
test('rejects an unpaired or malformed cursor before authentication', async () => {
const route = createClusterControlRunStepListRoute(
{
async findRunById() {
return run();
},
},
{
async listByRun() {
return { stepRuns: [], truncated: false };
},
},
);
let authentications = 0;
const pipeline = createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([route]),
authenticator: {
authenticate() {
authentications += 1;
return null;
},
},
policy: {
authorize() {
throw new Error('must not authorize');
},
},
audit: {
record() {
throw new Error('must not audit');
},
},
now: () => 10_000,
});
for (const query of [
{ after_step_key: ['build'] },
{ after_step_run_id: ['step-1'] },
{ after_step_key: ['bad value'], after_step_run_id: ['step-1'] },
{ after_step_key: ['build'], after_step_run_id: ['step-1'], limit: ['65'] },
]) {
await assert.rejects(
pipeline.prepare({
requestId: 'request-invalid-run-step-list',
method: 'GET',
path: '/api/v3/projects/prj_default/runs/run-1/steps',
query,
headers: {},
signal: new AbortController().signal,
}),
(error) =>
error.statusCode === 400 && error.code === 'invalid_route_query',
);
}
assert.equal(authentications, 0);
});
@@ -0,0 +1,115 @@
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterRuntimeSchedulerCoordinator,
} = require('../dist/scheduling/runtimeScheduler');
function schedulerSummary() {
return Object.freeze({
firstClaimAcquiredAtMs: null,
lastClaimAcquiredAtMs: null,
claimed: 0,
initialized: 0,
skipped: 0,
admitted: 0,
raced: 0,
saturated: false,
});
}
test('orders recovery and lost retry before the existing scheduler cadence', async () => {
const calls = [];
const coordinator = new ClusterRuntimeSchedulerCoordinator(
{
async reconcile() {
calls.push('recovery');
return { safe: true, remaining: 0, failed: 0 };
},
},
{
async reconcile() {
calls.push('lost-retry');
return {
scanned: 1,
scheduled: 1,
requeued: 0,
failed: 0,
raced: 0,
hasMore: false,
};
},
},
{
async scheduleOnce() {
calls.push('schedule');
return schedulerSummary();
},
},
);
assert.deepEqual(await coordinator.scheduleOnce(), schedulerSummary());
assert.deepEqual(calls, ['recovery', 'lost-retry', 'schedule']);
assert.deepEqual(coordinator.latestMaintenanceSummary(), {
recovery: { safe: true, remaining: 0, failed: 0 },
lostRetry: {
scanned: 1,
scheduled: 1,
requeued: 0,
failed: 0,
raced: 0,
hasMore: false,
},
});
});
test('coalesces overlapping cadence calls and fails closed before scheduling', async () => {
let release;
let recoveryCalls = 0;
let schedulerCalls = 0;
const coordinator = new ClusterRuntimeSchedulerCoordinator(
{
async reconcile() {
recoveryCalls += 1;
await new Promise((resolve) => {
release = resolve;
});
throw new Error('recovery unavailable');
},
},
{
async reconcile() {
throw new Error('lost retry must not run');
},
},
{
async scheduleOnce() {
schedulerCalls += 1;
return schedulerSummary();
},
},
);
const first = coordinator.scheduleOnce();
const second = coordinator.scheduleOnce();
assert.equal(first, second);
while (!release) await new Promise((resolve) => setImmediate(resolve));
release();
await assert.rejects(first, /recovery unavailable/);
assert.equal(recoveryCalls, 1);
assert.equal(schedulerCalls, 0);
assert.equal(coordinator.latestMaintenanceSummary(), undefined);
});
test('rejects incomplete maintenance capabilities', () => {
assert.throws(
() =>
new ClusterRuntimeSchedulerCoordinator(
{},
{ reconcile() {} },
{ scheduleOnce() {} },
),
/runtime scheduler coordinator is invalid/,
);
});
@@ -0,0 +1,98 @@
'use strict';
const assert = require('node:assert/strict');
const { createHash } = require('node:crypto');
const { test } = require('node:test');
const {
CreateBucketCommand,
DeleteBucketCommand,
DeleteObjectsCommand,
ListObjectsV2Command,
S3Client,
} = require('@aws-sdk/client-s3');
const {
S3ClusterRemoteWorkerArtifactStore,
S3ClusterRemoteWorkerArtifactStoreError,
} = require('@qinglong/cluster-control/s3-artifact-store');
const endpoint = process.env.QL3_TEST_S3_ENDPOINT;
const accessKeyId = process.env.QL3_TEST_S3_ACCESS_KEY_ID;
const secretAccessKey = process.env.QL3_TEST_S3_SECRET_ACCESS_KEY;
test('real S3-compatible service preserves immutable Artifact evidence', {
skip: endpoint && accessKeyId && secretAccessKey
? false
: 'requires QL3_TEST_S3_ENDPOINT and credentials',
}, async () => {
const client = new S3Client({
endpoint,
region: 'us-east-1',
forcePathStyle: true,
credentials: { accessKeyId, secretAccessKey },
});
const bucket = `ql3-artifact-${process.pid}-${Date.now()}`.slice(0, 63);
const command = Object.freeze({
projectId: 'project-s3-integration',
runId: 'run-s3-integration',
attemptId: 'attempt-s3-integration',
logArtifactId: `wlog-${'c'.repeat(30)}`,
byteLength: 17,
truncated: true,
});
const content = Buffer.from('real object bytes');
const body = (value) => Object.freeze({
async *[Symbol.asyncIterator]() {
yield value.subarray(0, 4);
yield value.subarray(4);
},
});
try {
await client.send(new CreateBucketCommand({ Bucket: bucket }));
const store = new S3ClusterRemoteWorkerArtifactStore({
client,
bucket,
prefix: 'qinglong/integration',
encryption: { mode: 's3' },
});
const stored = await store.put(command, body(content));
assert.equal(stored.status, 'stored');
assert.equal(
stored.sha256,
createHash('sha256').update(content).digest('hex'),
);
const replay = await store.put(command, body(content));
assert.equal(replay.status, 'already_stored');
await assert.rejects(
store.put(command, body(Buffer.from('REAL OBJECT BYTES'))),
(error) =>
error instanceof S3ClusterRemoteWorkerArtifactStoreError &&
error.reason === 'integrity_mismatch',
);
const objects = await client.send(new ListObjectsV2Command({
Bucket: bucket,
Prefix: 'qinglong/integration/',
}));
assert.equal(objects.KeyCount, 1);
assert.match(objects.Contents[0].Key, /\/objects\//);
} finally {
try {
const objects = await client.send(new ListObjectsV2Command({
Bucket: bucket,
}));
if (objects.Contents?.length) {
await client.send(new DeleteObjectsCommand({
Bucket: bucket,
Delete: {
Objects: objects.Contents.map(({ Key }) => ({ Key })),
Quiet: true,
},
}));
}
await client.send(new DeleteBucketCommand({ Bucket: bucket }));
} catch {
// Preserve the integration assertion; the ephemeral container is removed.
}
client.destroy();
}
});
@@ -0,0 +1,388 @@
'use strict';
const assert = require('node:assert/strict');
const { createHash } = require('node:crypto');
const { test } = require('node:test');
const {
CopyObjectCommand,
DeleteObjectCommand,
HeadObjectCommand,
PutObjectCommand,
} = require('@aws-sdk/client-s3');
const {
S3ClusterRemoteWorkerArtifactStore,
S3ClusterRemoteWorkerArtifactStoreError,
} = require('@qinglong/cluster-control/s3-artifact-store');
const TEMPORARY_ID = '018f62f6-7b41-4e4f-8cf8-6f38888629a2';
const COMMAND = Object.freeze({
projectId: 'project-1',
runId: 'run-1',
attemptId: 'attempt-1',
logArtifactId: `wlog-${'a'.repeat(30)}`,
byteLength: 11,
truncated: false,
});
const CONTENT = Buffer.from('hello world');
const CONTENT_SHA256 = createHash('sha256').update(CONTENT).digest('hex');
const LOOKUP = Object.freeze({
projectId: COMMAND.projectId,
runId: COMMAND.runId,
attemptId: COMMAND.attemptId,
logArtifactId: COMMAND.logArtifactId,
});
function checksum(content) {
return createHash('sha256').update(content).digest('base64');
}
function notFound() {
const error = new Error('not found');
error.name = 'NotFound';
error.$metadata = { httpStatusCode: 404 };
return error;
}
class MemoryS3Client {
constructor(options = {}) {
this.options = options;
this.objects = new Map();
this.commands = [];
}
async send(command) {
this.commands.push(command);
const input = command.input;
if (command instanceof HeadObjectCommand) {
const object = this.objects.get(input.Key);
if (!object) throw notFound();
const metadata = { ...object.metadata };
if (this.options.corruptFinalMetadata && input.Key.includes('/objects/')) {
metadata['ql3-content-sha256'] = '0'.repeat(64);
}
return {
ContentLength: object.content.byteLength,
ContentType: object.contentType,
ChecksumSHA256: this.options.corruptFinalChecksum &&
input.Key.includes('/objects/')
? Buffer.alloc(32, 9).toString('base64')
: checksum(object.content),
Metadata: metadata,
};
}
if (command instanceof PutObjectCommand) {
assert.equal(input.IfNoneMatch, '*');
assert.equal(input.ChecksumAlgorithm, 'SHA256');
assert.equal(
input.ServerSideEncryption,
this.options.expectedEncryption ?? 'AES256',
);
if (this.options.expectedKmsKeyId) {
assert.equal(input.SSEKMSKeyId, this.options.expectedKmsKeyId);
}
if (this.objects.has(input.Key)) {
const error = new Error('precondition failed');
error.$metadata = { httpStatusCode: 412 };
throw error;
}
const chunks = [];
for await (const chunk of input.Body) chunks.push(Buffer.from(chunk));
const content = Buffer.concat(chunks);
this.objects.set(input.Key, {
content,
contentType: input.ContentType,
metadata: { ...input.Metadata },
});
if (this.options.throwAfterPut) throw new Error('lost put response');
return { ChecksumSHA256: checksum(content) };
}
if (command instanceof CopyObjectCommand) {
assert.equal(input.IfNoneMatch, '*');
assert.equal(input.MetadataDirective, 'REPLACE');
assert.equal(input.ChecksumAlgorithm, 'SHA256');
const sourceKey = decodeURIComponent(input.CopySource)
.split('/')
.slice(1)
.join('/');
const source = this.objects.get(sourceKey);
if (!source) throw notFound();
const final = {
content: Buffer.from(source.content),
contentType: input.ContentType,
metadata: { ...input.Metadata },
};
if (this.options.raceOnCopy && !this.objects.has(input.Key)) {
this.objects.set(input.Key, final);
const error = new Error('conditional request conflict');
error.$metadata = { httpStatusCode: 409 };
throw error;
}
if (this.objects.has(input.Key)) {
const error = new Error('precondition failed');
error.$metadata = { httpStatusCode: 412 };
throw error;
}
this.objects.set(input.Key, final);
if (this.options.throwAfterCopy) throw new Error('lost copy response');
return { CopyObjectResult: { ChecksumSHA256: checksum(final.content) } };
}
if (command instanceof DeleteObjectCommand) {
if (this.options.failDelete) throw new Error('delete unavailable');
this.objects.delete(input.Key);
return {};
}
throw new Error(`unexpected command: ${command.constructor.name}`);
}
}
function store(client, overrides = {}) {
return new S3ClusterRemoteWorkerArtifactStore({
client,
bucket: 'ql3-artifacts-test',
prefix: 'tenant-a/worker-artifacts',
encryption: { mode: 's3' },
createTemporaryId: () => TEMPORARY_ID,
...overrides,
});
}
function chunks(content = CONTENT, observed) {
return Object.freeze({
async *[Symbol.asyncIterator]() {
yield content.subarray(0, 3);
yield content.subarray(3, 7);
yield content.subarray(7);
if (observed) observed.complete = true;
},
});
}
function permanentKey(client) {
return [...client.objects.keys()].find((key) => key.includes('/objects/'));
}
test('streams to a checksummed temporary object then conditionally promotes it', async () => {
const client = new MemoryS3Client();
const adapter = store(client);
const receipt = await adapter.put(COMMAND, chunks());
assert.deepEqual(receipt, {
status: 'stored',
...COMMAND,
sha256: CONTENT_SHA256,
});
assert.deepEqual(
client.commands.map((command) => command.constructor.name),
[
'HeadObjectCommand',
'PutObjectCommand',
'HeadObjectCommand',
'CopyObjectCommand',
'HeadObjectCommand',
'DeleteObjectCommand',
],
);
const key = permanentKey(client);
assert.match(key, /^tenant-a\/worker-artifacts\/objects\/[a-f0-9]{2}\/[a-f0-9]{64}$/);
assert.equal(key.includes(COMMAND.runId), false);
assert.equal(client.objects.size, 1);
const copy = client.commands.find((command) => command instanceof CopyObjectCommand);
assert.equal(copy.input.Metadata['ql3-content-sha256'], CONTENT_SHA256);
assert.equal(JSON.stringify(copy.input.Metadata).includes(COMMAND.projectId), false);
assert.equal(JSON.stringify(copy.input.Metadata).includes(COMMAND.runId), false);
const inspected = await adapter.inspect(LOOKUP);
assert.deepEqual(inspected, { ...receipt, status: 'already_stored' });
});
test('exact replay consumes and hashes the whole body without another write', async () => {
const client = new MemoryS3Client();
const adapter = store(client);
await adapter.put(COMMAND, chunks());
client.commands.length = 0;
const observed = { complete: false };
const replay = await adapter.put(COMMAND, chunks(CONTENT, observed));
assert.equal(replay.status, 'already_stored');
assert.equal(observed.complete, true);
assert.deepEqual(
client.commands.map((command) => command.constructor.name),
['HeadObjectCommand'],
);
await assert.rejects(
adapter.put(COMMAND, chunks(Buffer.from('HELLO WORLD'))),
(error) =>
error instanceof S3ClusterRemoteWorkerArtifactStoreError &&
error.reason === 'integrity_mismatch',
);
});
test('resolves a concurrent conditional-copy winner by immutable inspect', async () => {
const client = new MemoryS3Client({ raceOnCopy: true });
const receipt = await store(client).put(COMMAND, chunks());
assert.equal(receipt.status, 'already_stored');
assert.equal(receipt.sha256, CONTENT_SHA256);
assert.equal(client.objects.size, 1);
});
test('recovers lost put and copy responses only from checksum evidence', async () => {
for (const [option, expectedStatus] of [
['throwAfterPut', 'stored'],
['throwAfterCopy', 'already_stored'],
]) {
const client = new MemoryS3Client({ [option]: true });
const receipt = await store(client).put(COMMAND, chunks());
assert.equal(receipt.status, expectedStatus);
assert.equal(receipt.sha256, CONTENT_SHA256);
assert.equal(client.objects.size, 1);
}
});
test('rejects corrupt metadata, checksum, short and oversized content', async () => {
for (const option of ['corruptFinalMetadata', 'corruptFinalChecksum']) {
const client = new MemoryS3Client({ [option]: true });
await assert.rejects(
store(client).put(COMMAND, chunks()),
(error) =>
error instanceof S3ClusterRemoteWorkerArtifactStoreError &&
error.reason === 'integrity_mismatch',
);
}
for (const content of [Buffer.from('short'), Buffer.from('hello world!')]) {
const client = new MemoryS3Client();
await assert.rejects(
store(client).put(COMMAND, chunks(content)),
(error) => error instanceof S3ClusterRemoteWorkerArtifactStoreError,
);
assert.equal(permanentKey(client), undefined);
}
});
test('temporary cleanup failure is diagnostic and never reverses promotion', async () => {
const diagnostics = [];
const client = new MemoryS3Client({ failDelete: true });
const receipt = await store(client, {
onDiagnostic(error, context) {
diagnostics.push([error.message, context.operation]);
},
}).put(COMMAND, chunks());
assert.equal(receipt.status, 'stored');
assert.deepEqual(diagnostics, [[
'delete unavailable',
'temporary_object_cleanup',
]]);
assert.equal(client.objects.size, 2);
});
test('requires exact bucket, prefix, encryption and temporary ID configuration', async () => {
const client = new MemoryS3Client();
assert.throws(
() => new S3ClusterRemoteWorkerArtifactStore({
client,
bucket: 'Invalid_Bucket',
encryption: { mode: 's3' },
}),
/bucket is invalid/,
);
assert.throws(
() => new S3ClusterRemoteWorkerArtifactStore({
client,
bucket: 'valid-bucket',
prefix: '../escape',
encryption: { mode: 's3' },
}),
/prefix is invalid/,
);
assert.throws(
() => new S3ClusterRemoteWorkerArtifactStore({
client,
bucket: 'valid-bucket',
encryption: { mode: 'kms' },
}),
/encryption is invalid/,
);
await assert.rejects(
store(client, { createTemporaryId: () => '../invalid' }).put(
COMMAND,
chunks(),
),
(error) =>
error instanceof S3ClusterRemoteWorkerArtifactStoreError &&
error.reason === 'unavailable',
);
});
test('supports an empty immutable Artifact without synthesizing a chunk', async () => {
const client = new MemoryS3Client();
const command = {
...COMMAND,
logArtifactId: `wlog-${'b'.repeat(30)}`,
byteLength: 0,
truncated: undefined,
};
delete command.truncated;
const receipt = await store(client).put(command, {
async *[Symbol.asyncIterator]() {},
});
assert.equal(receipt.byteLength, 0);
assert.equal(receipt.sha256, createHash('sha256').digest('hex'));
assert.equal(Object.hasOwn(receipt, 'truncated'), false);
});
test('propagates KMS and expected-owner fences to both sides of promotion', async () => {
const keyId = 'arn:aws:kms:us-east-1:123456789012:key/test';
const client = new MemoryS3Client({
expectedEncryption: 'aws:kms',
expectedKmsKeyId: keyId,
});
await store(client, {
expectedBucketOwner: '123456789012',
encryption: { mode: 'kms', keyId },
}).put(COMMAND, chunks());
for (const command of client.commands) {
assert.equal(command.input.ExpectedBucketOwner, '123456789012');
}
const copy = client.commands.find(
(command) => command instanceof CopyObjectCommand,
);
assert.equal(copy.input.CopySourceExpectedBucketOwner, '123456789012');
assert.equal(copy.input.ServerSideEncryption, 'aws:kms');
assert.equal(copy.input.SSEKMSKeyId, keyId);
});
test('never deletes a colliding temporary object it cannot prove it owns', async () => {
const client = new MemoryS3Client();
const temporaryKey =
`tenant-a/worker-artifacts/temporary/${TEMPORARY_ID}`;
client.objects.set(temporaryKey, {
content: Buffer.from('other operation'),
contentType: 'application/octet-stream',
metadata: { 'ql3-schema': 'other' },
});
await assert.rejects(
store(client).put(COMMAND, chunks()),
(error) =>
error instanceof S3ClusterRemoteWorkerArtifactStoreError &&
error.reason === 'unavailable',
);
assert.equal(client.objects.has(temporaryKey), true);
assert.equal(
client.commands.some((command) => command instanceof DeleteObjectCommand),
false,
);
});
test('a pre-aborted request performs no object-store operation', async () => {
const client = new MemoryS3Client();
const controller = new AbortController();
const reason = new Error('stopping');
controller.abort(reason);
await assert.rejects(
store(client).put(COMMAND, chunks(), controller.signal),
(error) => error === reason,
);
assert.equal(client.commands.length, 0);
});
@@ -0,0 +1,41 @@
const assert = require('node:assert/strict');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { test } = require('node:test');
function inspectEntrypoint(specifier) {
const packageDirectory = path.resolve(__dirname, '..');
const script = `
const exported = require(${JSON.stringify(specifier)});
const loaded = Object.keys(require.cache).map((file) => file.replaceAll('\\\\', '/'));
process.stdout.write(JSON.stringify({
hasStore: typeof exported.S3ClusterRemoteWorkerArtifactStore === 'function',
loadedAwsSdk: loaded.some((file) => file.includes('/@aws-sdk/')),
}));
`;
const result = spawnSync(process.execPath, ['-e', script], {
cwd: packageDirectory,
encoding: 'utf8',
});
assert.equal(result.status, 0, result.stderr);
return JSON.parse(result.stdout);
}
test('default and production entrypoints do not load the S3 client', () => {
for (const specifier of [
'@qinglong/cluster-control',
'@qinglong/cluster-control/production',
]) {
const report = inspectEntrypoint(specifier);
assert.equal(report.hasStore, false, specifier);
assert.equal(report.loadedAwsSdk, false, specifier);
}
});
test('S3 artifact store is reachable only through its explicit subpath', () => {
const report = inspectEntrypoint(
'@qinglong/cluster-control/s3-artifact-store',
);
assert.equal(report.hasStore, true);
assert.equal(report.loadedAwsSdk, true);
});
@@ -0,0 +1,246 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterSchedulerCoordinator,
ClusterSchedulerLifecycle,
} = require('../dist/scheduling/scheduler');
function nextMinute(schedule, afterMs) {
if (schedule.expression !== '* * * * *' || schedule.timezone !== 'UTC') {
throw new Error('unsupported test schedule');
}
return Math.floor(afterMs / 60_000 + 1) * 60_000;
}
function claim(overrides = {}) {
return {
projectId: 'default',
triggerId: 'trigger-1',
triggerRevision: 1,
triggerContentDigest: 'a'.repeat(64),
triggerUpdatedAtMs: 1,
taskId: 'task-1',
taskRevision: 1,
taskContentDigest: 'b'.repeat(64),
expression: '* * * * *',
timezone: 'UTC',
misfirePolicy: 'skip',
stateVersion: 1,
nextFireAtMs: 60_000,
claimOwner: 'scheduler-a',
claimToken: '019f7800-0000-7000-8000-000000000001',
claimVersion: 1,
claimAcquiredAtMs: 61_000,
claimExpiresAtMs: 91_000,
...overrides,
};
}
function idFactory() {
let value = 0;
return () => `019f7800-0000-7000-8000-${String(++value).padStart(12, '0')}`;
}
test('claims a bounded cycle and notifies only fenced admissions', async () => {
const claims = [
claim(),
claim({
triggerId: 'trigger-2',
claimToken: '019f7800-0000-7000-8000-000000000002',
claimAcquiredAtMs: 62_000,
claimExpiresAtMs: 92_000,
}),
];
const claimCommands = [];
const commits = [];
const notified = [];
const coordinator = new ClusterSchedulerCoordinator(
{
async claimNextClusterSchedule(command) {
claimCommands.push(command);
const next = claims.shift();
return next
? {
...next,
claimOwner: command.ownerId,
claimToken: command.claimToken,
}
: null;
},
async commitClusterScheduleDecision(command) {
commits.push(command);
if (commits.length === 2) return { status: 'raced' };
return {
status: 'admitted',
disposition: 'admit',
runId: command.runId,
attemptId: command.attemptId,
};
},
},
{
ownerId: 'scheduler-a',
claimLeaseMs: 30_000,
maxClaimsPerCycle: 4,
misfireGraceMs: 5_000,
nextOccurrence: nextMinute,
createId: idFactory(),
onAdmitted: (runId, attemptId) => notified.push([runId, attemptId]),
},
);
assert.deepEqual(await coordinator.scheduleOnce(), {
firstClaimAcquiredAtMs: 61_000,
lastClaimAcquiredAtMs: 62_000,
claimed: 2,
initialized: 0,
skipped: 0,
admitted: 1,
raced: 1,
saturated: false,
});
assert.equal(claimCommands.length, 3);
assert.equal(
claimCommands.every(({ ownerId }) => ownerId === 'scheduler-a'),
true,
);
assert.equal(commits.length, 2);
assert.deepEqual(notified, [[commits[0].runId, commits[0].attemptId]]);
});
test('does not allocate Run identities for a skipped occurrence', async () => {
let claimed = false;
const commands = [];
let allocations = 0;
const skipped = claim({
nextFireAtMs: 60_000,
claimAcquiredAtMs: 900_000,
claimExpiresAtMs: 930_000,
});
const coordinator = new ClusterSchedulerCoordinator(
{
async claimNextClusterSchedule(command) {
if (claimed) return null;
claimed = true;
return {
...skipped,
claimOwner: command.ownerId,
claimToken: command.claimToken,
};
},
async commitClusterScheduleDecision(command) {
commands.push(command);
return { status: 'advanced', disposition: 'skip' };
},
},
{
ownerId: 'scheduler-a',
misfireGraceMs: 0,
nextOccurrence: nextMinute,
createId() {
allocations += 1;
return `019f7800-0000-7000-8000-${String(allocations).padStart(
12,
'0',
)}`;
},
},
);
const summary = await coordinator.scheduleOnce();
assert.equal(summary.skipped, 1);
assert.equal(allocations, 2);
assert.equal(commands[0].runId, undefined);
assert.equal(commands[0].attemptId, undefined);
});
test('marks a cycle saturated at its hard claim budget', async () => {
let version = 0;
const coordinator = new ClusterSchedulerCoordinator(
{
async claimNextClusterSchedule(command) {
version += 1;
return claim({
stateVersion: version,
claimVersion: version,
claimOwner: command.ownerId,
claimToken: command.claimToken,
});
},
async commitClusterScheduleDecision(command) {
return {
status: 'admitted',
disposition: 'admit',
runId: command.runId,
attemptId: command.attemptId,
};
},
},
{
ownerId: 'scheduler-a',
maxClaimsPerCycle: 2,
nextOccurrence: nextMinute,
createId: idFactory(),
},
);
const summary = await coordinator.scheduleOnce();
assert.equal(summary.claimed, 2);
assert.equal(summary.saturated, true);
});
test('rejects a node-local clock as scheduler authority', () => {
assert.throws(
() =>
new ClusterSchedulerCoordinator(
{
async claimNextClusterSchedule() {
return null;
},
async commitClusterScheduleDecision() {
return { status: 'raced' };
},
},
{
ownerId: 'scheduler-a',
clock: () => 1,
},
),
/options are invalid/,
);
});
test('lifecycle coalesces work and drains without overlapping cycles', async () => {
let calls = 0;
let release;
const pending = new Promise((resolve) => {
release = resolve;
});
const summary = {
firstClaimAcquiredAtMs: null,
lastClaimAcquiredAtMs: null,
claimed: 0,
initialized: 0,
skipped: 0,
admitted: 0,
raced: 0,
saturated: false,
};
const lifecycle = new ClusterSchedulerLifecycle(
{
async scheduleOnce() {
calls += 1;
await pending;
return summary;
},
},
{ intervalMs: 250, stopTimeoutMs: 1_000 },
);
assert.equal(lifecycle.start(), 'started');
const first = lifecycle.runOnce();
assert.equal(lifecycle.runOnce(), first);
const stopping = lifecycle.stopAndDrain();
release();
assert.deepEqual(await first, summary);
assert.deepEqual(await stopping, { status: 'stopped' });
assert.equal(calls, 1);
await assert.rejects(lifecycle.runOnce(), /stopping/);
});
@@ -0,0 +1,155 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_CONTROL_TASK_LIST_ROUTE,
createClusterControlTaskListRoute,
} = require('@qinglong/cluster-control/task-routes');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
function task(taskId, overrides = {}) {
return {
projectId: 'prj_default',
taskId,
revision: 2,
name: `Task ${taskId}`,
description: 'secret-adjacent',
kind: 'command',
spec: { schema: 'qinglong/command@v1', config: { command: ['private'] } },
labels: { private: 'value' },
enabled: true,
mutationId: 'mutation-private',
contentDigest: 'digest-private',
createdAtMs: 10,
updatedAtMs: 20,
...overrides,
};
}
function authorized(query = {}, body = null) {
return {
projectId: 'prj_default',
request: { query, body },
};
}
test('publishes one reviewed bounded Task list route', () => {
assert.deepEqual(CLUSTER_CONTROL_TASK_LIST_ROUTE, {
method: 'GET',
path: '/api/v3/projects/{projectId}/tasks',
operationId: 'task.list',
permission: 'task.read',
projectParameter: 'projectId',
allowedQuery: ['after_task_id', 'limit'],
});
assert.throws(() => createClusterControlTaskListRoute({}), TypeError);
});
test('parses an exact keyset page and returns the shared projection', async () => {
const calls = [];
const route = createClusterControlTaskListRoute({
async listTaskDefinitions(query) {
calls.push(query);
return {
definitions: [task('task-b', { enabled: false })],
truncated: true,
next: { taskId: 'task-b' },
};
},
});
const result = await route.handle(
authorized({ limit: ['8'], after_task_id: ['task-a'] }),
{},
);
assert.deepEqual(calls, [
{
projectId: 'prj_default',
limit: 8,
after: { taskId: 'task-a' },
},
]);
assert.deepEqual(result, {
statusCode: 200,
body: {
tasks: [
{
taskId: 'task-b',
revision: 2,
name: 'Task task-b',
kind: 'command',
specSchema: 'qinglong/command@v1',
enabled: false,
updatedAtMs: 20,
},
],
hasMore: true,
next: { taskId: 'task-b' },
},
});
assert.equal(JSON.stringify(result).includes('secret-adjacent'), false);
assert.equal(JSON.stringify(result).includes('private'), false);
});
test('rejects malformed query and body and masks repository failures', async () => {
const route = createClusterControlTaskListRoute({
async listTaskDefinitions() { throw new Error('offline'); },
});
for (const query of [
{ limit: ['08'] },
{ limit: ['65'] },
{ limit: ['8', '9'] },
{ after_task_id: ['-bad'] },
]) {
assert.deepEqual(await route.handle(authorized(query), {}), {
statusCode: 400,
body: { code: 'invalid_task_list_query' },
});
}
assert.deepEqual(await route.handle(authorized({}, { value: true }), {}), {
statusCode: 400,
body: { code: 'invalid_request_body' },
});
assert.deepEqual(await route.handle(authorized({}), {}), {
statusCode: 503,
body: { code: 'task_list_unavailable' },
});
});
test('rejects non-canonical pagination before authentication', async () => {
let authentications = 0;
const pipeline = createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([
createClusterControlTaskListRoute({
async listTaskDefinitions() {
return { definitions: [], truncated: false };
},
}),
]),
authenticator: {
authenticate() {
authentications += 1;
return null;
},
},
policy: { authorize() { throw new Error('must not authorize'); } },
audit: { record() { throw new Error('must not audit'); } },
now: () => 10_000,
});
await assert.rejects(
pipeline.prepare({
requestId: 'request-invalid-task-list',
method: 'GET',
path: '/api/v3/projects/prj_default/tasks',
query: { limit: ['08'] },
headers: {},
signal: new AbortController().signal,
}),
(error) => error.statusCode === 400 && error.code === 'invalid_route_query',
);
assert.equal(authentications, 0);
});
@@ -0,0 +1,150 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
CLUSTER_CONTROL_TASK_READ_ROUTE,
createClusterControlTaskReadRoute,
} = require('@qinglong/cluster-control/task-routes');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
const {
createTaskDefinitionRecord,
} = require('@qinglong/runtime-core/task-definition');
function task(overrides = {}) {
return createTaskDefinitionRecord(
{
projectId: 'prj_default',
taskId: 'task-a',
expectedRevision: null,
mutationId: '123e4567-e89b-42d3-a456-426614174201',
name: 'Task A',
description: 'secret-adjacent',
kind: 'command',
spec: {
schema: 'qinglong/command@v1',
config: { command: { kind: 'shell', command: 'private' } },
},
labels: { private: 'value' },
enabled: true,
occurredAtMs: 20,
...overrides,
},
10,
);
}
function authorized(body = null, query = {}) {
return {
projectId: 'prj_default',
request: { body, query },
};
}
test('publishes one reviewed current Task route', () => {
assert.deepEqual(CLUSTER_CONTROL_TASK_READ_ROUTE, {
method: 'GET',
path: '/api/v3/projects/{projectId}/tasks/{taskId}',
operationId: 'task.get',
permission: 'task.read',
projectParameter: 'projectId',
});
assert.throws(() => createClusterControlTaskReadRoute({}), TypeError);
});
test('returns the shared projection and masks absent or cross-Project Tasks', async () => {
const definition = task({ enabled: false });
const calls = [];
const route = createClusterControlTaskReadRoute({
async findCurrentTaskDefinition(projectId, taskId) {
calls.push([projectId, taskId]);
return taskId === 'task-a' ? definition : null;
},
});
const result = await route.handle(authorized(), { taskId: 'task-a' });
assert.deepEqual(calls, [['prj_default', 'task-a']]);
assert.equal(result.statusCode, 200);
assert.deepEqual(result.body.task, {
taskId: 'task-a',
revision: 1,
name: 'Task A',
kind: 'command',
specSchema: 'qinglong/command@v1',
enabled: false,
contentDigest: definition.contentDigest,
createdAtMs: 10,
updatedAtMs: 20,
});
assert.equal(JSON.stringify(result).includes('private'), false);
assert.deepEqual(
await route.handle(authorized(), { taskId: 'task-absent' }),
{ statusCode: 404, body: { code: 'task_not_found' } },
);
const crossProject = createClusterControlTaskReadRoute({
async findCurrentTaskDefinition() { return task({ projectId: 'other' }); },
});
assert.deepEqual(
await crossProject.handle(authorized(), { taskId: 'task-a' }),
{ statusCode: 404, body: { code: 'task_not_found' } },
);
});
test('rejects body and fails closed on corrupt or unavailable storage', async () => {
const definition = task();
const corrupt = createClusterControlTaskReadRoute({
async findCurrentTaskDefinition() {
return { ...definition, contentDigest: '0'.repeat(64) };
},
});
assert.deepEqual(
await corrupt.handle(authorized(), { taskId: 'task-a' }),
{ statusCode: 503, body: { code: 'task_query_unavailable' } },
);
assert.deepEqual(
await corrupt.handle(authorized({ invalid: true }), { taskId: 'task-a' }),
{ statusCode: 400, body: { code: 'invalid_request_body' } },
);
const unavailable = createClusterControlTaskReadRoute({
async findCurrentTaskDefinition() { throw new Error('offline'); },
});
assert.deepEqual(
await unavailable.handle(authorized(), { taskId: 'task-a' }),
{ statusCode: 503, body: { code: 'task_query_unavailable' } },
);
});
test('rejects query before authentication', async () => {
let authentications = 0;
const pipeline = createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([
createClusterControlTaskReadRoute({
async findCurrentTaskDefinition() { return null; },
}),
]),
authenticator: {
authenticate() {
authentications += 1;
return null;
},
},
policy: { authorize() { throw new Error('must not authorize'); } },
audit: { record() { throw new Error('must not audit'); } },
now: () => 10_000,
});
await assert.rejects(
pipeline.prepare({
requestId: 'request-invalid-task-get',
method: 'GET',
path: '/api/v3/projects/prj_default/tasks/task-a',
query: { expanded: ['true'] },
headers: {},
signal: new AbortController().signal,
}),
(error) => error.statusCode === 400 && error.code === 'invalid_route_query',
);
assert.equal(authentications, 0);
});
@@ -0,0 +1,174 @@
'use strict';
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
TASK_START_SCHEMA,
TaskStartFenceRejectedError,
TaskStartNotFoundError,
TaskStartUnavailableError,
} = require('@qinglong/runtime-core/task-start');
const {
createClusterControlAdmissionPipeline,
} = require('@qinglong/cluster-control/admission');
const {
createClusterControlRouteRegistry,
} = require('@qinglong/cluster-control/routes');
const {
CLUSTER_CONTROL_TASK_START_ROUTE,
createClusterControlTaskStartRoute,
} = require('@qinglong/cluster-control/task-routes');
const IDS = [
'019f7300-0000-7000-8000-000000000601',
'019f7300-0000-7000-8000-000000000602',
'019f7300-0000-7000-8000-000000000603',
'019f7300-0000-7000-8000-000000000604',
];
const MUTATION_ID = '019f7300-0000-7000-8000-000000000600';
const DIGEST = 'a'.repeat(64);
const PRINCIPAL = Object.freeze({
subject: Object.freeze({ type: 'user', id: 'user-1' }),
authenticationId: 'session:user-1',
authenticatedAtMs: 9_000,
expiresAtMs: 11_000,
assurance: 'single_factor',
});
const METADATA = Object.freeze({
requestId: 'request-task-start',
method: 'POST',
path: '/api/v3/projects/project-1/tasks/task-1/runs',
query: Object.freeze({}),
headers: Object.freeze({ authorization: 'Bearer opaque' }),
signal: new AbortController().signal,
});
function receipt(overrides = {}) {
return {
status: 'accepted',
projectId: 'project-1',
taskId: 'task-1',
taskRevision: 3,
taskContentDigest: DIGEST,
runId: IDS[0],
attemptId: IDS[1],
runStatus: 'queued',
runVersion: 2,
eventSequence: 2,
executorType: 'remote_worker',
executionRevisionDigest: 'b'.repeat(64),
createdAtMs: 10_000,
...overrides,
};
}
function pipeline(repository, events = [], fence = {
projectVersion: 2,
bindingVersion: 3,
}) {
let index = 0;
return createClusterControlAdmissionPipeline({
routes: createClusterControlRouteRegistry([
createClusterControlTaskStartRoute(repository, () => IDS[index++]),
]),
authenticator: {
authenticate() {
events.push('authenticate');
return PRINCIPAL;
},
},
policy: {
authorize(request) {
events.push(`authorize:${request.permission}:${request.projectId}`);
return { effect: 'allow', reasons: ['role_grant'], fence };
},
},
audit: {
record(record) {
events.push(`audit:${record.outcome}:${record.operationId}`);
},
},
now: () => 10_000,
});
}
function body(overrides = {}) {
return {
schema: TASK_START_SCHEMA,
mutationId: MUTATION_ID,
expectedRevision: 3,
expectedContentDigest: DIGEST,
...overrides,
};
}
test('publishes one reviewed run.start Task route', () => {
assert.deepEqual(CLUSTER_CONTROL_TASK_START_ROUTE, {
method: 'POST',
path: '/api/v3/projects/{projectId}/tasks/{taskId}/runs',
operationId: 'task.start',
permission: 'run.start',
projectParameter: 'projectId',
});
});
test('authenticates, authorizes and audits before starting the exact Task', async () => {
const events = [];
let observed;
const prepared = await pipeline({
async startTask(command) {
events.push('repository');
observed = command;
return receipt();
},
}, events).prepare(METADATA);
assert.deepEqual(events, [
'authenticate',
'authorize:run.start:project-1',
'audit:allowed:task.start',
]);
assert.deepEqual(await prepared.handle(body()), {
statusCode: 202,
body: { schema: TASK_START_SCHEMA, ...receipt() },
});
assert.deepEqual(observed, {
projectId: 'project-1',
taskId: 'task-1',
mutationId: MUTATION_ID,
expectedRevision: 3,
expectedContentDigest: DIGEST,
runId: IDS[0],
attemptId: IDS[1],
createdEventId: IDS[2],
queuedEventId: IDS[3],
subject: PRINCIPAL.subject,
policyFence: { projectVersion: 2, bindingVersion: 3 },
});
});
test('rejects command injection and maps replay plus stable failures', async () => {
let calls = 0;
const invalid = await pipeline({
async startTask() { calls += 1; return receipt(); },
}).prepare(METADATA);
assert.equal((await invalid.handle(body({ command: '/bin/sh' }))).statusCode, 400);
assert.equal(calls, 0);
const replay = await pipeline({
async startTask() { return receipt({ status: 'existing' }); },
}).prepare(METADATA);
assert.equal((await replay.handle(body())).statusCode, 200);
for (const [error, statusCode, code] of [
[new TaskStartNotFoundError(), 404, 'task_not_found'],
[new TaskStartFenceRejectedError('definition_changed'), 409, 'task_start_fence_rejected'],
[new TaskStartUnavailableError(), 503, 'task_start_unavailable'],
]) {
const prepared = await pipeline({
async startTask() { throw error; },
}).prepare(METADATA);
const result = await prepared.handle(body());
assert.equal(result.statusCode, statusCode);
assert.equal(result.body.code, code);
}
});
@@ -0,0 +1,104 @@
const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
WorkerCredentialUnavailableError,
} = require('@qinglong/runtime-core/worker-credential');
const {
workerCredentialSecretDigest,
} = require('@qinglong/runtime-core/worker-credential-token');
const {
createWorkerCredentialAuthenticator,
} = require('@qinglong/cluster-control/worker-ingress');
const NOW = 10_000;
const PEPPER = Buffer.alloc(32, 1).toString('base64url');
const SECRET = Buffer.alloc(32, 2).toString('base64url');
const CREDENTIAL_ID = 'worker_primary';
function metadata(authorization = `Worker ql3w_${CREDENTIAL_ID}_${SECRET}`) {
return {
requestId: 'request-1',
method: 'POST',
path: '/api/v3/worker-ingress/workers/edge-1/sessions/018f5c64-9b9d-7f1a-8c2d-1234567890ac/heartbeat',
query: Object.freeze({}),
headers: Object.freeze({ authorization }),
signal: new AbortController().signal,
};
}
function credential(overrides = {}) {
return {
credentialId: CREDENTIAL_ID,
version: 2,
state: 'active',
workerId: 'edge-1',
secretDigest: workerCredentialSecretDigest(PEPPER, CREDENTIAL_ID, SECRET),
createdAtMs: 1,
notBeforeAtMs: 1,
expiresAtMs: 100_000,
...overrides,
};
}
function authenticator(value = credential(), overrides = {}) {
return createWorkerCredentialAuthenticator(
{ async resolve(id) { assert.equal(id, CREDENTIAL_ID); return value; } },
PEPPER,
{ now: () => NOW, ...overrides },
);
}
test('authenticates a ql3w credential as its bound short-lived Worker', async () => {
assert.deepEqual(await authenticator().authenticate(metadata()), {
workerId: 'edge-1',
credentialId: CREDENTIAL_ID,
credentialVersion: 2,
authenticationId: `worker_credential:${CREDENTIAL_ID}:2`,
authenticatedAtMs: NOW,
expiresAtMs: 70_000,
});
});
test('rejects malformed, mismatched, inactive and expired Worker credentials', async () => {
let calls = 0;
const strict = createWorkerCredentialAuthenticator(
{ async resolve() { calls += 1; return credential(); } },
PEPPER,
{ now: () => NOW },
);
for (const authorization of [
undefined,
'Bearer ql3w_worker_primary_invalid',
`Worker ql3w_${CREDENTIAL_ID}_${Buffer.alloc(32, 3).toString('base64url')}`,
]) {
const request = metadata();
request.headers = Object.freeze(authorization ? { authorization } : {});
assert.equal(await strict.authenticate(request), null);
}
assert.equal(calls, 1);
for (const value of [
null,
credential({ state: 'revoked' }),
credential({ notBeforeAtMs: NOW + 1 }),
credential({ expiresAtMs: NOW }),
]) assert.equal(await authenticator(value).authenticate(metadata()), null);
});
test('maps storage, corrupt records and cancellation to unavailable', async () => {
const unavailable = createWorkerCredentialAuthenticator(
{ async resolve() { throw new Error('database unavailable'); } },
PEPPER,
{ now: () => NOW },
);
await assert.rejects(unavailable.authenticate(metadata()), WorkerCredentialUnavailableError);
await assert.rejects(
authenticator(credential({ secretDigest: 'corrupt' })).authenticate(metadata()),
WorkerCredentialUnavailableError,
);
const controller = new AbortController();
controller.abort();
await assert.rejects(
authenticator().authenticate({ ...metadata(), signal: controller.signal }),
WorkerCredentialUnavailableError,
);
});

Some files were not shown because too many files have changed in this diff Show More