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
@@ -0,0 +1,317 @@
import {
PrivateLocalCommandFileError,
readPrivateLocalCommandFile,
} from '@qinglong/local-command-file';
import {
openLocalOwnerDeliveryAcknowledgementGc,
type CompactLocalOwnerDeliveryAcknowledgementRequest,
type OpenLocalOwnerDeliveryAcknowledgementGcOptions,
} from '../security-maintenance/acknowledgementGc';
import {
openLocalOwnerPepperMaterialGc,
type CollectLocalOwnerPepperMaterialRequest,
type OpenLocalOwnerPepperMaterialGcOptions,
} from '../security-maintenance/pepperGc';
import type {
LocalOwnerPromptOutputGcAuthority,
OpenLocalOwnerPromptOutputGcOptions,
} from '../prompt-output-maintenance/promptOutputGc';
import type {
LocalOwnerPromptOutputKeyRetirementAuthority,
OpenLocalOwnerPromptOutputKeyRetirementOptions,
RetireLocalOwnerPromptOutputKeyRequest,
} from '../prompt-output-maintenance/promptOutputKeyRetirement';
export interface LocalOwnerAcknowledgementGcCommand {
readonly schemaVersion: 1;
readonly operation: 'owner.delivery-acknowledgement.compact';
readonly options: OpenLocalOwnerDeliveryAcknowledgementGcOptions;
readonly request: CompactLocalOwnerDeliveryAcknowledgementRequest;
}
export interface LocalOwnerPepperMaterialGcCommand {
readonly schemaVersion: 1;
readonly operation: 'owner.pepper-material.collect';
readonly options: OpenLocalOwnerPepperMaterialGcOptions;
readonly request: CollectLocalOwnerPepperMaterialRequest;
}
export interface LocalOwnerPromptOutputGcCommand {
readonly schemaVersion: 1;
readonly operation: 'owner.prompt-output.collect';
readonly options: OpenLocalOwnerPromptOutputGcOptions;
readonly request: Readonly<Record<string, never>>;
}
export interface LocalOwnerPromptOutputKeyRetirementCommand {
readonly schemaVersion: 1;
readonly operation: 'owner.prompt-output-key.retire';
readonly options: OpenLocalOwnerPromptOutputKeyRetirementOptions;
readonly request: RetireLocalOwnerPromptOutputKeyRequest;
}
export type LocalOwnerGcCommand =
| LocalOwnerAcknowledgementGcCommand
| LocalOwnerPepperMaterialGcCommand
| LocalOwnerPromptOutputGcCommand
| LocalOwnerPromptOutputKeyRetirementCommand;
export interface LocalOwnerAcknowledgementGcCommandResult {
readonly schemaVersion: 1;
readonly operation: 'owner.delivery-acknowledgement.compact';
readonly status: 'inserted' | 'existing';
readonly gcMutationId: string;
readonly acknowledgementMutationId: string;
readonly acknowledgementKind: 'credential' | 'challenge';
readonly retentionEligibleAtMs: number;
readonly compactedAtMs: number;
}
export interface LocalOwnerPepperMaterialGcCommandResult {
readonly schemaVersion: 1;
readonly operation: 'owner.pepper-material.collect';
readonly status: 'inserted' | 'existing';
readonly prepareMutationId: string;
readonly completeMutationId: string;
readonly pepperKeyId: string;
readonly state: 'completed';
readonly completedAtMs: number;
}
export interface LocalOwnerPromptOutputGcCommandResult {
readonly schemaVersion: 1;
readonly operation: 'owner.prompt-output.collect';
readonly scanned: number;
readonly tombstoned: number;
readonly skipped: number;
readonly hasMore: boolean;
}
export interface LocalOwnerPromptOutputKeyRetirementCommandResult {
readonly schemaVersion: 1;
readonly operation: 'owner.prompt-output-key.retire';
readonly status: 'completed' | 'existing';
readonly keyId: string;
readonly retirementId: string;
readonly preparationDigest: string;
readonly completionDigest: string;
readonly completedAtMs: number;
}
export type LocalOwnerGcCommandResult =
| LocalOwnerAcknowledgementGcCommandResult
| LocalOwnerPepperMaterialGcCommandResult
| LocalOwnerPromptOutputGcCommandResult
| LocalOwnerPromptOutputKeyRetirementCommandResult;
export interface LocalOwnerGcCommandRunnerDependencies {
readonly openAcknowledgementGc: typeof openLocalOwnerDeliveryAcknowledgementGc;
readonly openPepperMaterialGc: typeof openLocalOwnerPepperMaterialGc;
readonly openPromptOutputGc: (
options: OpenLocalOwnerPromptOutputGcOptions,
) => Promise<LocalOwnerPromptOutputGcAuthority>;
readonly openPromptOutputKeyRetirement: (
options: OpenLocalOwnerPromptOutputKeyRetirementOptions,
) => Promise<LocalOwnerPromptOutputKeyRetirementAuthority>;
}
export interface LocalOwnerGcCommandRunner {
run(commandFilePath: string): Promise<Readonly<LocalOwnerGcCommandResult>>;
}
export class LocalOwnerGcCliConfigurationError extends TypeError {
readonly code = 'LOCAL_OWNER_GC_CLI_CONFIGURATION_INVALID';
constructor(message: string, readonly cause?: unknown) {
super(`Local Owner GC CLI configuration is invalid: ${message}`);
this.name = 'LocalOwnerGcCliConfigurationError';
}
}
function exactKeys(value: object, expected: readonly string[]): boolean {
const actual = Object.keys(value).sort();
const canonical = [...expected].sort();
return (
actual.length === canonical.length &&
actual.every((key, index) => key === canonical[index])
);
}
function normalizeCommand(value: unknown): Readonly<LocalOwnerGcCommand> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
!exactKeys(value, ['schemaVersion', 'operation', 'options', 'request'])
) {
throw new LocalOwnerGcCliConfigurationError('command shape is invalid');
}
const candidate = value as Record<string, unknown>;
if (
candidate.schemaVersion !== 1 ||
(candidate.operation !== 'owner.delivery-acknowledgement.compact' &&
candidate.operation !== 'owner.pepper-material.collect' &&
candidate.operation !== 'owner.prompt-output.collect' &&
candidate.operation !== 'owner.prompt-output-key.retire') ||
!candidate.options ||
typeof candidate.options !== 'object' ||
Array.isArray(candidate.options) ||
!candidate.request ||
typeof candidate.request !== 'object' ||
Array.isArray(candidate.request)
) {
throw new LocalOwnerGcCliConfigurationError('command value is invalid');
}
return Object.freeze(value as LocalOwnerGcCommand);
}
function readCommandFile(candidatePath: string): Readonly<LocalOwnerGcCommand> {
try {
return normalizeCommand(readPrivateLocalCommandFile(candidatePath));
} catch (error) {
if (error instanceof LocalOwnerGcCliConfigurationError) throw error;
if (error instanceof PrivateLocalCommandFileError) {
throw new LocalOwnerGcCliConfigurationError(
'command file cannot be read',
error,
);
}
throw new LocalOwnerGcCliConfigurationError(
'command file cannot be read',
error,
);
}
}
function dependencies(
value: LocalOwnerGcCommandRunnerDependencies,
): Readonly<LocalOwnerGcCommandRunnerDependencies> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
!exactKeys(value, [
'openAcknowledgementGc',
'openPepperMaterialGc',
'openPromptOutputGc',
'openPromptOutputKeyRetirement',
]) ||
typeof value.openAcknowledgementGc !== 'function' ||
typeof value.openPepperMaterialGc !== 'function' ||
typeof value.openPromptOutputGc !== 'function' ||
typeof value.openPromptOutputKeyRetirement !== 'function'
) {
throw new LocalOwnerGcCliConfigurationError(
'runner dependencies are invalid',
);
}
return Object.freeze({ ...value });
}
export function createLocalOwnerGcCommandRunner(
candidateDependencies: LocalOwnerGcCommandRunnerDependencies = {
openAcknowledgementGc: openLocalOwnerDeliveryAcknowledgementGc,
openPepperMaterialGc: openLocalOwnerPepperMaterialGc,
async openPromptOutputGc(options) {
const { openLocalOwnerPromptOutputGc } = await import(
'../prompt-output-maintenance/promptOutputGc.js'
);
return openLocalOwnerPromptOutputGc(options);
},
async openPromptOutputKeyRetirement(options) {
const { openLocalOwnerPromptOutputKeyRetirement } = await import(
'../prompt-output-maintenance/promptOutputKeyRetirement.js'
);
return openLocalOwnerPromptOutputKeyRetirement(options);
},
},
): LocalOwnerGcCommandRunner {
const adapters = dependencies(candidateDependencies);
return Object.freeze({
async run(commandFilePath: string) {
const command = readCommandFile(commandFilePath);
if (command.operation === 'owner.delivery-acknowledgement.compact') {
const authority = await adapters.openAcknowledgementGc(command.options);
try {
const result = await authority.compact(command.request);
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
status: result.status,
gcMutationId: result.record.mutationId,
acknowledgementMutationId: result.record.acknowledgementMutationId,
acknowledgementKind: result.record.acknowledgementKind,
retentionEligibleAtMs: result.record.retentionEligibleAtMs,
compactedAtMs: result.record.compactedAtMs,
});
} finally {
await authority.close();
}
}
if (command.operation === 'owner.prompt-output.collect') {
if (Object.keys(command.request).length !== 0) {
throw new LocalOwnerGcCliConfigurationError(
'Prompt output collection request must be empty',
);
}
const authority = await adapters.openPromptOutputGc(command.options);
try {
const result = await authority.collect();
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
...result,
});
} finally {
await authority.close();
}
}
if (command.operation === 'owner.prompt-output-key.retire') {
const authority = await adapters.openPromptOutputKeyRetirement(
command.options,
);
try {
const result = await authority.retire(command.request);
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
...result,
});
} finally {
await authority.close();
}
}
const authority = await adapters.openPepperMaterialGc(command.options);
try {
const result = await authority.collect(command.request);
if (
result.record.state !== 'completed' ||
!result.record.completeMutationId ||
result.record.completedAtMs === undefined
) {
throw new LocalOwnerGcCliConfigurationError(
'pepper material collection did not complete',
);
}
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
status: result.status,
prepareMutationId: result.record.prepareMutationId,
completeMutationId: result.record.completeMutationId,
pepperKeyId: result.record.pepperKeyId,
state: result.record.state,
completedAtMs: result.record.completedAtMs,
});
} finally {
await authority.close();
}
},
});
}
export function runLocalOwnerGcCommandFile(
commandFilePath: string,
): Promise<Readonly<LocalOwnerGcCommandResult>> {
return createLocalOwnerGcCommandRunner().run(commandFilePath);
}
@@ -0,0 +1,49 @@
#!/usr/bin/env node
import { runLocalOwnerGcCommandFile } from './application-command/localOwnerMaintenanceCommand';
const USAGE =
'Usage: ql3-owner-gc run --command-file /absolute/private-command.json';
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 !== 3 || argv[0] !== 'run' || argv[1] !== '--command-file') {
process.stderr.write(
`${JSON.stringify({
code: 'LOCAL_OWNER_GC_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const result = await runLocalOwnerGcCommandFile(argv[2]!);
process.stdout.write(`${JSON.stringify(result)}\n`);
} catch (error) {
const candidate = error as {
readonly code?: unknown;
readonly name?: unknown;
readonly message?: unknown;
};
process.stderr.write(
`${JSON.stringify({
code:
typeof candidate.code === 'string'
? candidate.code
: 'LOCAL_OWNER_GC_CLI_FAILED',
name: typeof candidate.name === 'string' ? candidate.name : 'Error',
message:
typeof candidate.message === 'string'
? candidate.message
: 'Local Owner GC command failed',
})}\n`,
);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,69 @@
import { assertLocalModelInvocationFeatureReady } from '@qinglong/ai/model-invocation-migration';
import { LocalPluginPackagePromptOutputGarbageCollector } from '@qinglong/ai/local-plugin-package-prompt-output-retention-storage';
import {
createPluginPackagePromptOutputRetentionPolicyCatalogResolver,
type PluginPackagePromptOutputRetentionPolicyCatalog,
} from '@qinglong/ai/plugin-package-prompt-output-retention';
import {
openLocalSqliteOptionalFeatureRuntimeDatabase,
type LocalSqliteDatabaseOptions,
type LocalSqliteProfile,
} from '@qinglong/local-sqlite/optional-feature-runtime';
export interface OpenLocalOwnerPromptOutputGcOptions
extends LocalSqliteDatabaseOptions {
readonly retentionPolicyCatalog: PluginPackagePromptOutputRetentionPolicyCatalog;
readonly limit?: number;
}
export interface LocalOwnerPromptOutputGcAuthority {
readonly profile: LocalSqliteProfile;
collect(): Promise<Readonly<{
scanned: number;
tombstoned: number;
skipped: number;
hasMore: boolean;
}>>;
close(): Promise<void>;
}
export async function openLocalOwnerPromptOutputGc(
options: OpenLocalOwnerPromptOutputGcOptions,
): Promise<LocalOwnerPromptOutputGcAuthority> {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('Local Prompt output GC options are invalid');
}
const policies =
createPluginPackagePromptOutputRetentionPolicyCatalogResolver(
options.retentionPolicyCatalog,
);
const database = await openLocalSqliteOptionalFeatureRuntimeDatabase({
databasePath: options.databasePath,
profile: options.profile,
...(options.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: options.busyTimeoutMs }),
});
try {
assertLocalModelInvocationFeatureReady(database.authority.client);
const collector = new LocalPluginPackagePromptOutputGarbageCollector({
authority: database.authority,
policies,
...(options.limit === undefined ? {} : { limit: options.limit }),
});
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: database.profile,
collect() {
return collector.collect();
},
close() {
closePromise ??= database.close();
return closePromise;
},
});
} catch (cause) {
await database.close();
throw cause;
}
}
@@ -0,0 +1,88 @@
import { assertLocalModelInvocationFeatureReady } from '@qinglong/ai/model-invocation-migration';
import { PluginPackagePromptOutputFileKeyring } from '@qinglong/ai/plugin-package-prompt-output-file-keyring';
import { PluginPackagePromptOutputKeyRetirementCoordinator } from '@qinglong/ai/plugin-package-prompt-output-key-retirement';
import { LocalPluginPackagePromptOutputKeyRetirementRepository } from '@qinglong/ai/local-plugin-package-prompt-output-key-retirement-storage';
import {
openLocalSqliteOptionalFeatureRuntimeDatabase,
type LocalSqliteDatabaseOptions,
type LocalSqliteProfile,
} from '@qinglong/local-sqlite/optional-feature-runtime';
export interface OpenLocalOwnerPromptOutputKeyRetirementOptions
extends LocalSqliteDatabaseOptions {
readonly keyringPath: string;
}
export interface RetireLocalOwnerPromptOutputKeyRequest {
readonly keyId: string;
readonly retirementId: string;
readonly requestId: string;
readonly mutationId: string;
}
export interface LocalOwnerPromptOutputKeyRetirementAuthority {
readonly profile: LocalSqliteProfile;
retire(request: RetireLocalOwnerPromptOutputKeyRequest): Promise<Readonly<{
status: 'completed' | 'existing';
keyId: string;
retirementId: string;
preparationDigest: string;
completionDigest: string;
completedAtMs: number;
}>>;
close(): Promise<void>;
}
export async function openLocalOwnerPromptOutputKeyRetirement(
options: OpenLocalOwnerPromptOutputKeyRetirementOptions,
): Promise<LocalOwnerPromptOutputKeyRetirementAuthority> {
if (
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
typeof options.keyringPath !== 'string'
) {
throw new TypeError('Local Prompt output key retirement options are invalid');
}
const database = await openLocalSqliteOptionalFeatureRuntimeDatabase({
databasePath: options.databasePath,
profile: options.profile,
...(options.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: options.busyTimeoutMs }),
});
try {
assertLocalModelInvocationFeatureReady(database.authority.client);
const keys = new PluginPackagePromptOutputFileKeyring(options.keyringPath);
const repository =
new LocalPluginPackagePromptOutputKeyRetirementRepository({
authority: database.authority,
});
const coordinator = new PluginPackagePromptOutputKeyRetirementCoordinator({
repository,
materials: keys,
});
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: database.profile,
async retire(request: RetireLocalOwnerPromptOutputKeyRequest) {
const result = await coordinator.retire(request);
return Object.freeze({
status: result.status,
keyId: result.preparation.keyId,
retirementId: result.preparation.retirementId,
preparationDigest: result.preparation.preparationDigest,
completionDigest: result.completion.completionDigest,
completedAtMs: result.completion.completedAtMs,
});
},
close() {
closePromise ??= database.close();
return closePromise;
},
});
} catch (cause) {
await database.close();
throw cause;
}
}
@@ -0,0 +1,269 @@
import { FileLocalOwnerBootstrapSecretDelivery } from '@qinglong/local-owner-console/secret-delivery';
import {
openLocalSqliteAcknowledgementGcDatabase,
type LocalSqliteAcknowledgementGcDatabase,
type LocalSqliteDatabaseOptions,
type LocalSqliteProfile,
} from '@qinglong/local-sqlite/acknowledgement-gc';
import {
localOwnerDeliveryAcknowledgementGcRetentionPolicyDigest,
type LocalOwnerDeliveryAcknowledgementGcRecord,
type LocalOwnerDeliveryAcknowledgementGcRepository,
type LocalOwnerDeliveryAcknowledgementGcRetentionPolicy,
} from '@qinglong/runtime-core/local-owner-delivery-acknowledgement-gc';
const UUID_V4_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
const REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const DIGEST_PATTERN = /^[0-9a-f]{64}$/;
export interface CompactLocalOwnerDeliveryAcknowledgementRequest {
readonly mutationId: string;
readonly requestId: string;
readonly acknowledgementMutationId: string;
readonly expectedKind: 'credential' | 'challenge';
readonly expectedDeliveryDigest: string;
}
export interface CompactLocalOwnerDeliveryAcknowledgementResult {
readonly status: 'inserted' | 'existing';
readonly record: Readonly<LocalOwnerDeliveryAcknowledgementGcRecord>;
}
export interface LocalOwnerDeliveryAcknowledgementGcService {
compact(
request: CompactLocalOwnerDeliveryAcknowledgementRequest,
): Promise<Readonly<CompactLocalOwnerDeliveryAcknowledgementResult>>;
}
export interface CreateLocalOwnerDeliveryAcknowledgementGcServiceOptions {
readonly secretDeliveryDirectory: string;
readonly retentionPolicy: LocalOwnerDeliveryAcknowledgementGcRetentionPolicy;
}
export interface OpenLocalOwnerDeliveryAcknowledgementGcOptions
extends LocalSqliteDatabaseOptions,
CreateLocalOwnerDeliveryAcknowledgementGcServiceOptions {}
export interface LocalOwnerDeliveryAcknowledgementGcAuthority
extends LocalOwnerDeliveryAcknowledgementGcService {
readonly profile: LocalSqliteProfile;
close(): Promise<void>;
}
export class LocalOwnerDeliveryAcknowledgementGcConfigurationError extends TypeError {
readonly code =
'LOCAL_OWNER_DELIVERY_ACKNOWLEDGEMENT_GC_CONFIGURATION_INVALID';
constructor(message: string, readonly cause?: unknown) {
super(
`Local Owner delivery acknowledgement GC configuration is invalid: ${message}`,
);
this.name = 'LocalOwnerDeliveryAcknowledgementGcConfigurationError';
}
}
function exactKeys(value: object, expected: readonly string[]): boolean {
const actual = Object.keys(value).sort();
const canonical = [...expected].sort();
return (
actual.length === canonical.length &&
actual.every((key, index) => key === canonical[index])
);
}
function request(
value: CompactLocalOwnerDeliveryAcknowledgementRequest,
): Readonly<CompactLocalOwnerDeliveryAcknowledgementRequest> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
!exactKeys(value, [
'mutationId',
'requestId',
'acknowledgementMutationId',
'expectedKind',
'expectedDeliveryDigest',
]) ||
!UUID_V4_PATTERN.test(value.mutationId) ||
!UUID_V4_PATTERN.test(value.acknowledgementMutationId) ||
value.mutationId === value.acknowledgementMutationId ||
!REQUEST_ID_PATTERN.test(value.requestId) ||
(value.expectedKind !== 'credential' &&
value.expectedKind !== 'challenge') ||
!DIGEST_PATTERN.test(value.expectedDeliveryDigest)
) {
throw new LocalOwnerDeliveryAcknowledgementGcConfigurationError(
'request shape is invalid',
);
}
return Object.freeze({ ...value });
}
function options(
value: CreateLocalOwnerDeliveryAcknowledgementGcServiceOptions,
): Readonly<CreateLocalOwnerDeliveryAcknowledgementGcServiceOptions> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
!exactKeys(value, ['secretDeliveryDirectory', 'retentionPolicy'])
) {
throw new LocalOwnerDeliveryAcknowledgementGcConfigurationError(
'options shape is invalid',
);
}
try {
localOwnerDeliveryAcknowledgementGcRetentionPolicyDigest(
value.retentionPolicy,
);
} catch (error) {
throw new LocalOwnerDeliveryAcknowledgementGcConfigurationError(
'retentionPolicy is invalid',
error,
);
}
return Object.freeze({
secretDeliveryDirectory: value.secretDeliveryDirectory,
retentionPolicy: Object.freeze({ ...value.retentionPolicy }),
});
}
function sameExisting(
record: Readonly<LocalOwnerDeliveryAcknowledgementGcRecord>,
value: Readonly<CompactLocalOwnerDeliveryAcknowledgementRequest>,
retentionPolicy: LocalOwnerDeliveryAcknowledgementGcRetentionPolicy,
): boolean {
return (
record.mutationId === value.mutationId &&
record.requestId === value.requestId &&
record.acknowledgementMutationId === value.acknowledgementMutationId &&
record.acknowledgementKind === value.expectedKind &&
record.deliveryDigest === value.expectedDeliveryDigest &&
record.retentionPolicyDigest ===
localOwnerDeliveryAcknowledgementGcRetentionPolicyDigest(retentionPolicy)
);
}
function audit(eventId: string, requestId: string, occurredAtMs: number) {
return Object.freeze({
eventId,
requestId,
operationId: 'owner.delivery_acknowledgement.gc',
projectId: null,
subject: Object.freeze({
type: 'system' as const,
id: 'owner-acknowledgement-gc',
}),
authenticationId: 'local-owner-console',
outcome: 'allowed' as const,
reasons: Object.freeze(['delivery_acknowledgement_gc']),
fence: null,
occurredAtMs,
});
}
export function createLocalOwnerDeliveryAcknowledgementGcService(
repository: LocalOwnerDeliveryAcknowledgementGcRepository,
candidateOptions: CreateLocalOwnerDeliveryAcknowledgementGcServiceOptions,
): LocalOwnerDeliveryAcknowledgementGcService {
if (
!repository ||
typeof repository.resolveByAcknowledgement !== 'function' ||
typeof repository.compact !== 'function'
) {
throw new LocalOwnerDeliveryAcknowledgementGcConfigurationError(
'repository boundary is invalid',
);
}
const settings = options(candidateOptions);
const delivery = new FileLocalOwnerBootstrapSecretDelivery(
settings.secretDeliveryDirectory,
);
return Object.freeze({
async compact(candidate: CompactLocalOwnerDeliveryAcknowledgementRequest) {
const command = request(candidate);
const existing = await repository.resolveByAcknowledgement(
command.acknowledgementMutationId,
);
if (existing) {
if (!sameExisting(existing, command, settings.retentionPolicy)) {
throw new LocalOwnerDeliveryAcknowledgementGcConfigurationError(
'request conflicts with the durable GC record',
);
}
return Object.freeze({ status: 'existing' as const, record: existing });
}
const evidence = delivery.inspectBridgeClear(
command.expectedKind,
command.acknowledgementMutationId,
);
return repository.compact({
...command,
bridgeClearEvidence: evidence,
retentionPolicy: settings.retentionPolicy,
compactedAtMs: evidence.inspectedAtMs,
audit: audit(
command.mutationId,
command.requestId,
evidence.inspectedAtMs,
),
});
},
});
}
export async function openLocalOwnerDeliveryAcknowledgementGc(
candidate: OpenLocalOwnerDeliveryAcknowledgementGcOptions,
): Promise<LocalOwnerDeliveryAcknowledgementGcAuthority> {
if (
!candidate ||
typeof candidate !== 'object' ||
Array.isArray(candidate) ||
!exactKeys(candidate, [
'databasePath',
'profile',
'secretDeliveryDirectory',
'retentionPolicy',
...(candidate.busyTimeoutMs === undefined ? [] : ['busyTimeoutMs']),
])
) {
throw new LocalOwnerDeliveryAcknowledgementGcConfigurationError(
'open options are invalid',
);
}
const settings = options({
secretDeliveryDirectory: candidate.secretDeliveryDirectory,
retentionPolicy: candidate.retentionPolicy,
});
const databaseOptions: LocalSqliteDatabaseOptions = {
databasePath: candidate.databasePath,
profile: candidate.profile,
...(candidate.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: candidate.busyTimeoutMs }),
};
let database: LocalSqliteAcknowledgementGcDatabase | null = null;
try {
database = await openLocalSqliteAcknowledgementGcDatabase(databaseOptions);
const service = createLocalOwnerDeliveryAcknowledgementGcService(
database.acknowledgementGc,
settings,
);
const owned = database;
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: owned.profile,
compact(request: CompactLocalOwnerDeliveryAcknowledgementRequest) {
return service.compact(request);
},
close() {
return (closePromise ??= owned.close());
},
});
} catch (error) {
await database?.close().catch(() => undefined);
throw error;
}
}
@@ -0,0 +1,450 @@
import { createHash } from 'node:crypto';
import path from 'node:path';
import {
LocalOwnerPepperKeyringFileProvider,
type LocalOwnerPepperKeyMaterial,
} from '@qinglong/local-owner-console/pepper-custody';
import {
destroyLocalOwnerPepperKey,
type DestroyLocalOwnerPepperKeyResult,
} from '@qinglong/local-owner-console/pepper-custody/destructive';
import {
openLocalSqlitePepperGcDatabase,
type LocalSqliteDatabaseOptions,
type LocalSqlitePepperGcDatabase,
type LocalSqliteProfile,
} from '@qinglong/local-sqlite/pepper-gc';
import { assertApiCredentialPepperKeyId } from '@qinglong/runtime-core/api-credential';
import {
type LocalOwnerPepperActivationRecord,
type LocalOwnerPepperKeyRecord,
type LocalOwnerPepperReferenceRepository,
} from '@qinglong/runtime-core/local-owner-pepper';
import {
localOwnerPepperMaterialGcRetentionPolicyDigest,
type LocalOwnerPepperMaterialGcRecord,
type LocalOwnerPepperMaterialGcRepository,
type LocalOwnerPepperMaterialGcRetentionPolicy,
} from '@qinglong/runtime-core/local-owner-pepper-material-gc';
const UUID_V4_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
const REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
type LocalOwnerPepperGcCatalog = Pick<
LocalOwnerPepperReferenceRepository,
'resolveKey' | 'resolveActive'
>;
export interface CollectLocalOwnerPepperMaterialRequest {
readonly prepareMutationId: string;
readonly prepareRequestId: string;
readonly completeMutationId: string;
readonly completeRequestId: string;
readonly pepperKeyId: string;
}
export interface CollectLocalOwnerPepperMaterialResult {
readonly status: 'inserted' | 'existing';
readonly record: Readonly<LocalOwnerPepperMaterialGcRecord>;
readonly runtimeMaterial: Readonly<DestroyLocalOwnerPepperKeyResult>;
readonly backupMaterial: Readonly<DestroyLocalOwnerPepperKeyResult>;
}
export interface LocalOwnerPepperMaterialGcService {
collect(
request: CollectLocalOwnerPepperMaterialRequest,
): Promise<Readonly<CollectLocalOwnerPepperMaterialResult>>;
}
export interface CreateLocalOwnerPepperMaterialGcServiceOptions {
readonly keyringDirectory: string;
readonly backupDirectory: string;
readonly retentionPolicy: LocalOwnerPepperMaterialGcRetentionPolicy;
}
export interface OpenLocalOwnerPepperMaterialGcOptions
extends LocalSqliteDatabaseOptions,
CreateLocalOwnerPepperMaterialGcServiceOptions {}
export interface LocalOwnerPepperMaterialGcAuthority
extends LocalOwnerPepperMaterialGcService {
readonly profile: LocalSqliteProfile;
close(): Promise<void>;
}
export class LocalOwnerPepperMaterialGcConfigurationError extends TypeError {
readonly code = 'LOCAL_OWNER_PEPPER_MATERIAL_GC_CONFIGURATION_INVALID';
constructor(message: string, readonly cause?: unknown) {
super(
`Local Owner pepper material GC configuration is invalid: ${message}`,
);
this.name = 'LocalOwnerPepperMaterialGcConfigurationError';
}
}
export class LocalOwnerPepperMaterialGcMaterialUnavailableError extends Error {
readonly code = 'LOCAL_OWNER_PEPPER_MATERIAL_GC_MATERIAL_UNAVAILABLE';
constructor() {
super(
'Local Owner pepper material or its independent backup is unavailable',
);
this.name = 'LocalOwnerPepperMaterialGcMaterialUnavailableError';
}
}
function exactKeys(value: object, expected: readonly string[]): boolean {
const actual = Object.keys(value).sort();
const canonical = [...expected].sort();
return (
actual.length === canonical.length &&
actual.every((key, index) => key === canonical[index])
);
}
function request(
value: CollectLocalOwnerPepperMaterialRequest,
): Readonly<CollectLocalOwnerPepperMaterialRequest> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
!exactKeys(value, [
'prepareMutationId',
'prepareRequestId',
'completeMutationId',
'completeRequestId',
'pepperKeyId',
]) ||
!UUID_V4_PATTERN.test(value.prepareMutationId) ||
!UUID_V4_PATTERN.test(value.completeMutationId) ||
value.prepareMutationId === value.completeMutationId ||
!REQUEST_ID_PATTERN.test(value.prepareRequestId) ||
!REQUEST_ID_PATTERN.test(value.completeRequestId)
) {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'request shape is invalid',
);
}
try {
assertApiCredentialPepperKeyId(value.pepperKeyId);
} catch {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'pepperKeyId is invalid',
);
}
return Object.freeze({ ...value });
}
function currentTime(): number {
const value = Date.now();
if (!Number.isSafeInteger(value) || value < 0) {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'trusted clock is invalid',
);
}
return value;
}
function options(
value: CreateLocalOwnerPepperMaterialGcServiceOptions,
): Readonly<CreateLocalOwnerPepperMaterialGcServiceOptions> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
!exactKeys(value, [
'keyringDirectory',
'backupDirectory',
'retentionPolicy',
]) ||
typeof value.keyringDirectory !== 'string' ||
typeof value.backupDirectory !== 'string' ||
!path.isAbsolute(value.keyringDirectory) ||
!path.isAbsolute(value.backupDirectory) ||
path.normalize(value.keyringDirectory) !== value.keyringDirectory ||
path.normalize(value.backupDirectory) !== value.backupDirectory ||
value.keyringDirectory === value.backupDirectory
) {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'options shape is invalid',
);
}
try {
localOwnerPepperMaterialGcRetentionPolicyDigest(value.retentionPolicy);
} catch (error) {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'retentionPolicy is invalid',
error,
);
}
return Object.freeze({
keyringDirectory: value.keyringDirectory,
backupDirectory: value.backupDirectory,
retentionPolicy: Object.freeze({ ...value.retentionPolicy }),
});
}
function audit(
eventId: string,
requestId: string,
operation: 'prepare' | 'complete',
occurredAtMs: number,
) {
return Object.freeze({
eventId,
requestId,
operationId: `owner.pepper.material_gc.${operation}`,
projectId: null,
subject: Object.freeze({ type: 'system' as const, id: 'owner-pepper-gc' }),
authenticationId: 'local-owner-console',
outcome: 'allowed' as const,
reasons: Object.freeze(['pepper_material_gc']),
fence: null,
occurredAtMs,
});
}
function materialMatches(
material: Readonly<LocalOwnerPepperKeyMaterial> | null,
expectedDigest: string | undefined,
): material is Readonly<LocalOwnerPepperKeyMaterial> {
return (
!!material && !!expectedDigest && material.summary.digest === expectedDigest
);
}
async function verifiedActive(
catalog: LocalOwnerPepperGcCatalog,
runtime: LocalOwnerPepperKeyringFileProvider,
backup: LocalOwnerPepperKeyringFileProvider,
): Promise<Readonly<LocalOwnerPepperActivationRecord>> {
const active = await catalog.resolveActive();
if (!active) {
throw new LocalOwnerPepperMaterialGcMaterialUnavailableError();
}
const key = await catalog.resolveKey(active.activePepperKeyId);
if (
!key ||
key.state !== 'active' ||
key.materialDigest !== active.materialDigest ||
key.backupDigest !== active.backupDigest ||
!materialMatches(
runtime.resolve(active.activePepperKeyId),
active.materialDigest,
) ||
!materialMatches(
backup.resolve(active.activePepperKeyId),
active.backupDigest,
)
) {
throw new LocalOwnerPepperMaterialGcMaterialUnavailableError();
}
return active;
}
function verifiedRetired(
key: Readonly<LocalOwnerPepperKeyRecord> | null,
runtime: LocalOwnerPepperKeyringFileProvider,
backup: LocalOwnerPepperKeyringFileProvider,
pepperKeyId: string,
): Readonly<LocalOwnerPepperKeyRecord> {
if (
!key ||
key.state !== 'retired' ||
!key.materialDigest ||
!key.backupDigest ||
!materialMatches(runtime.resolve(pepperKeyId), key.materialDigest) ||
!materialMatches(backup.resolve(pepperKeyId), key.backupDigest)
) {
throw new LocalOwnerPepperMaterialGcMaterialUnavailableError();
}
return key;
}
function combinedProof(
runtime: Readonly<DestroyLocalOwnerPepperKeyResult>,
backup: Readonly<DestroyLocalOwnerPepperKeyResult>,
): string {
return createHash('sha256')
.update('qinglong.local-owner-pepper-material-gc-completion.v1\0', 'utf8')
.update(runtime.destructionProofDigest, 'utf8')
.update('\0', 'utf8')
.update(backup.destructionProofDigest, 'utf8')
.digest('hex');
}
export function createLocalOwnerPepperMaterialGcService(
repository: LocalOwnerPepperMaterialGcRepository,
catalog: LocalOwnerPepperGcCatalog,
candidateOptions: CreateLocalOwnerPepperMaterialGcServiceOptions,
): LocalOwnerPepperMaterialGcService {
if (
!repository ||
typeof repository.resolve !== 'function' ||
typeof repository.prepare !== 'function' ||
typeof repository.complete !== 'function' ||
!catalog ||
typeof catalog.resolveKey !== 'function' ||
typeof catalog.resolveActive !== 'function'
) {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'repository boundary is invalid',
);
}
const settings = options(candidateOptions);
return Object.freeze({
async collect(candidate: CollectLocalOwnerPepperMaterialRequest) {
const command = request(candidate);
const runtimeProvider = new LocalOwnerPepperKeyringFileProvider(
settings.keyringDirectory,
);
const backupProvider = new LocalOwnerPepperKeyringFileProvider(
settings.backupDirectory,
);
let record = await repository.resolve(command.prepareMutationId);
if (!record) {
const target = verifiedRetired(
await catalog.resolveKey(command.pepperKeyId),
runtimeProvider,
backupProvider,
command.pepperKeyId,
);
const active = await verifiedActive(
catalog,
runtimeProvider,
backupProvider,
);
const preparedAtMs = currentTime();
record = (
await repository.prepare({
mutationId: command.prepareMutationId,
requestId: command.prepareRequestId,
pepperKeyId: command.pepperKeyId,
expectedMaterialDigest: target.materialDigest!,
expectedBackupMaterialDigest: target.backupDigest!,
expectedActivePepperKeyId: active.activePepperKeyId,
expectedActiveGeneration: active.generation,
expectedActiveMaterialDigest: active.materialDigest,
retentionPolicy: settings.retentionPolicy,
preparedAtMs,
audit: audit(
command.prepareMutationId,
command.prepareRequestId,
'prepare',
preparedAtMs,
),
})
).record;
}
if (
record.prepareMutationId !== command.prepareMutationId ||
record.prepareRequestId !== command.prepareRequestId ||
record.pepperKeyId !== command.pepperKeyId ||
record.retentionPolicyDigest !==
localOwnerPepperMaterialGcRetentionPolicyDigest(
settings.retentionPolicy,
)
) {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'request conflicts with the durable GC record',
);
}
await verifiedActive(catalog, runtimeProvider, backupProvider);
const runtimeMaterial = destroyLocalOwnerPepperKey({
keyringDirectory: settings.keyringDirectory,
pepperKeyId: record.pepperKeyId,
materialRole: 'runtime',
expectedMaterialDigest: record.materialDigest,
prepareMutationId: record.prepareMutationId,
});
const backupMaterial = destroyLocalOwnerPepperKey({
keyringDirectory: settings.backupDirectory,
pepperKeyId: record.pepperKeyId,
materialRole: 'backup',
expectedMaterialDigest: record.backupMaterialDigest,
prepareMutationId: record.prepareMutationId,
});
const completedAtMs =
record.completedAtMs ?? Math.max(currentTime(), record.preparedAtMs);
const completion = await repository.complete({
prepareMutationId: record.prepareMutationId,
mutationId: command.completeMutationId,
requestId: command.completeRequestId,
destructionProofDigest: combinedProof(runtimeMaterial, backupMaterial),
completedAtMs,
audit: audit(
command.completeMutationId,
command.completeRequestId,
'complete',
completedAtMs,
),
});
return Object.freeze({
status: completion.status,
record: completion.record,
runtimeMaterial,
backupMaterial,
});
},
});
}
export async function openLocalOwnerPepperMaterialGc(
candidate: OpenLocalOwnerPepperMaterialGcOptions,
): Promise<LocalOwnerPepperMaterialGcAuthority> {
if (
!candidate ||
typeof candidate !== 'object' ||
Array.isArray(candidate) ||
!exactKeys(candidate, [
'databasePath',
'profile',
'keyringDirectory',
'backupDirectory',
'retentionPolicy',
...(candidate.busyTimeoutMs === undefined ? [] : ['busyTimeoutMs']),
])
) {
throw new LocalOwnerPepperMaterialGcConfigurationError(
'open options are invalid',
);
}
const settings = options({
keyringDirectory: candidate.keyringDirectory,
backupDirectory: candidate.backupDirectory,
retentionPolicy: candidate.retentionPolicy,
});
const databaseOptions: LocalSqliteDatabaseOptions = {
databasePath: candidate.databasePath,
profile: candidate.profile,
...(candidate.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: candidate.busyTimeoutMs }),
};
let database: LocalSqlitePepperGcDatabase | null = null;
try {
database = await openLocalSqlitePepperGcDatabase(databaseOptions);
const service = createLocalOwnerPepperMaterialGcService(
database.materialGc,
database.ownerPepper,
settings,
);
const owned = database;
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: owned.profile,
collect(request: CollectLocalOwnerPepperMaterialRequest) {
return service.collect(request);
},
close() {
return (closePromise ??= owned.close());
},
});
} catch (error) {
await database?.close().catch(() => undefined);
throw error;
}
}