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,161 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { test } = require('node:test');
const {
openLocalSqliteBootstrapDatabase,
} = require('@qinglong/local-sqlite/bootstrap');
const { migrateLocalSqlitePath } = require('@qinglong/local-sqlite/migration');
const {
MIN_LOCAL_OWNER_ACKNOWLEDGEMENT_AUDIT_RETENTION_MS,
MIN_LOCAL_OWNER_ACKNOWLEDGEMENT_REPLAY_RETENTION_MS,
} = require('@qinglong/runtime-core/local-owner-delivery-acknowledgement-gc');
const {
LocalOwnerDeliveryAcknowledgementGcConfigurationError,
openLocalOwnerDeliveryAcknowledgementGc,
} = require('../dist/security-maintenance/acknowledgementGc');
const NOW = 1_760_000_000_000;
const ACK_MUTATION_ID = '00000000-0000-4000-8000-000000000e01';
const GC_MUTATION_ID = '00000000-0000-4000-8000-000000000e02';
const DELIVERY_DIGEST = 'd'.repeat(64);
const POLICY = Object.freeze({
version: 1,
replayRetentionMs: MIN_LOCAL_OWNER_ACKNOWLEDGEMENT_REPLAY_RETENTION_MS,
auditRetentionMs: MIN_LOCAL_OWNER_ACKNOWLEDGEMENT_AUDIT_RETENTION_MS,
});
const COMPACTED_AT_MS =
NOW + MIN_LOCAL_OWNER_ACKNOWLEDGEMENT_AUDIT_RETENTION_MS + 1_000;
function request(overrides = {}) {
return {
mutationId: GC_MUTATION_ID,
requestId: 'ack-gc-e02',
acknowledgementMutationId: ACK_MUTATION_ID,
expectedKind: 'credential',
expectedDeliveryDigest: DELIVERY_DIGEST,
...overrides,
};
}
async function fixture(t) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-ack-gc-e2e-'));
const secretDeliveryDirectory = path.join(root, 'secrets');
const databasePath = path.join(root, 'qinglong3.sqlite');
fs.mkdirSync(secretDeliveryDirectory, { mode: 0o700 });
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const databaseOptions = { databasePath, profile: 'edge' };
await migrateLocalSqlitePath(databaseOptions);
const database = await openLocalSqliteBootstrapDatabase(databaseOptions);
await database.ownerPepper.register({
mutationId: '00000000-0000-4000-8000-000000000e91',
pepperKeyId: 'legacy-v1',
materialDigest: 'a'.repeat(64),
backupDigest: 'b'.repeat(64),
registeredAtMs: NOW - 2_000,
});
await database.ownerPepper.activate({
mutationId: '00000000-0000-4000-8000-000000000e92',
pepperKeyId: 'legacy-v1',
expectedGeneration: 0,
activatedAtMs: NOW - 1_000,
});
const subjectId = `usr_${Buffer.alloc(16, 41).toString('base64url')}`;
const credentialId = `own_${Buffer.alloc(16, 42).toString('base64url')}`;
await database.ownerBootstrap.provision({
mutationId: ACK_MUTATION_ID,
requestId: 'provision-e01',
identity: {
subject: { type: 'user', id: subjectId },
status: 'active',
version: 1,
createdAtMs: NOW,
updatedAtMs: NOW,
},
credential: {
credentialId,
version: 1,
pepperKeyId: 'legacy-v1',
state: 'active',
subject: { type: 'user', id: subjectId },
subjectStatus: 'active',
secretDigest: 'c'.repeat(64),
createdAtMs: NOW,
notBeforeAtMs: NOW,
expiresAtMs: NOW + 600_000,
},
issuer: {
subject: { type: 'system', id: 'owner-bootstrap' },
authenticationId: 'local-console-test',
authenticatedAtMs: NOW - 1_000,
expiresAtMs: NOW + 60_000,
assurance: 'local_console',
},
audit: {
eventId: ACK_MUTATION_ID,
requestId: 'provision-e01',
operationId: 'identity.bootstrap_provision',
projectId: null,
subject: { type: 'system', id: 'owner-bootstrap' },
authenticationId: 'local-console-test',
outcome: 'allowed',
reasons: ['local_console_provisioning'],
fence: null,
occurredAtMs: NOW,
},
createdAtMs: NOW,
});
await database.ownerBootstrap.recordDeliveryAcknowledgement({
kind: 'credential',
mutationId: ACK_MUTATION_ID,
requestId: 'provision-e01',
subjectId,
credentialId,
factDigest: 'c'.repeat(64),
deliveryDigest: DELIVERY_DIGEST,
ttlMs: 600_000,
acknowledgedAtMs: NOW + 1,
});
await database.close();
return {
databasePath,
profile: 'edge',
secretDeliveryDirectory,
retentionPolicy: POLICY,
};
}
test('derives trusted bridge evidence and replays one durable compaction', async (t) => {
const options = await fixture(t);
t.mock.method(Date, 'now', () => COMPACTED_AT_MS);
const authority = await openLocalOwnerDeliveryAcknowledgementGc(options);
t.after(() => authority.close());
const inserted = await authority.compact(request());
assert.equal(inserted.status, 'inserted');
assert.equal(inserted.record.compactedAtMs, COMPACTED_AT_MS);
assert.equal(inserted.record.bridgeClearEvidenceDigest.length, 64);
const replay = await authority.compact(request());
assert.equal(replay.status, 'existing');
assert.deepEqual(replay.record, inserted.record);
});
test('rejects a live file bridge and caller-controlled time', async (t) => {
const options = await fixture(t);
t.mock.method(Date, 'now', () => COMPACTED_AT_MS);
fs.writeFileSync(
path.join(
options.secretDeliveryDirectory,
`credential-${ACK_MUTATION_ID}.ready.json`,
),
'{}',
{ mode: 0o600 },
);
const authority = await openLocalOwnerDeliveryAcknowledgementGc(options);
t.after(() => authority.close());
await assert.rejects(authority.compact(request()), /bridge is not clear/);
await assert.rejects(
authority.compact({ ...request(), compactedAtMs: COMPACTED_AT_MS }),
LocalOwnerDeliveryAcknowledgementGcConfigurationError,
);
});
@@ -0,0 +1,334 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { test } = require('node:test');
const {
LocalOwnerGcCliConfigurationError,
createLocalOwnerGcCommandRunner,
} = require('@qinglong/local-owner-maintenance/command');
const {
pluginPackagePromptOutputArtifactRetentionPolicyDigest,
} = require('../../ql3-ai/dist/prompt-output/pluginPackagePromptOutputArtifact');
function privateCommand(t, value) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-owner-gc-cli-'));
const filePath = path.join(root, 'command.json');
fs.writeFileSync(filePath, `${JSON.stringify(value)}\n`, { mode: 0o600 });
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
return filePath;
}
function acknowledgementCommand(overrides = {}) {
return {
schemaVersion: 1,
operation: 'owner.delivery-acknowledgement.compact',
options: {
databasePath: '/private/ql3.sqlite',
profile: 'edge',
secretDeliveryDirectory: '/private/secrets',
retentionPolicy: {
version: 1,
replayRetentionMs: 2_592_000_000,
auditRetentionMs: 2_592_000_000,
},
},
request: {
mutationId: '00000000-0000-4000-8000-000000000a02',
requestId: 'gc-a02',
acknowledgementMutationId: '00000000-0000-4000-8000-000000000a01',
expectedKind: 'credential',
expectedDeliveryDigest: 'd'.repeat(64),
},
...overrides,
};
}
function dependencies(state) {
return {
async openAcknowledgementGc(options) {
state.ackOptions = options;
return {
profile: 'edge',
async compact(request) {
state.ackRequest = request;
return {
status: 'inserted',
record: {
mutationId: request.mutationId,
requestId: request.requestId,
acknowledgementMutationId: request.acknowledgementMutationId,
acknowledgementKind: request.expectedKind,
deliveryDigest: request.expectedDeliveryDigest,
acknowledgedAtMs: 100,
acknowledgementSemanticDigest: 'a'.repeat(64),
bridgeClearEvidenceDigest: 'b'.repeat(64),
retentionPolicy: options.retentionPolicy,
retentionPolicyDigest: 'c'.repeat(64),
retentionEligibleAtMs: 200,
compactedAtMs: 300,
},
};
},
async close() {
state.ackClosed = true;
},
};
},
async openPepperMaterialGc(options) {
state.pepperOptions = options;
return {
profile: 'standalone',
async collect(request) {
state.pepperRequest = request;
return {
status: 'existing',
record: {
prepareMutationId: request.prepareMutationId,
prepareRequestId: request.prepareRequestId,
pepperKeyId: request.pepperKeyId,
materialDigest: 'a'.repeat(64),
backupMaterialDigest: 'b'.repeat(64),
activePepperKeyId: 'active-v2',
activeGeneration: 2,
activeMaterialDigest: 'c'.repeat(64),
retentionPolicy: options.retentionPolicy,
retentionPolicyDigest: 'd'.repeat(64),
referencesInspectedAtMs: 100,
retentionEligibleAtMs: 200,
preparedAtMs: 300,
state: 'completed',
completeMutationId: request.completeMutationId,
completeRequestId: request.completeRequestId,
destructionProofDigest: 'e'.repeat(64),
completedAtMs: 400,
},
runtimeMaterial: {},
backupMaterial: {},
};
},
async close() {
state.pepperClosed = true;
},
};
},
async openPromptOutputGc(options) {
state.promptOptions = options;
return {
profile: options.profile,
async collect() {
state.promptCollected = true;
return {
scanned: 3,
tombstoned: 2,
skipped: 1,
hasMore: false,
};
},
async close() {
state.promptClosed = true;
},
};
},
async openPromptOutputKeyRetirement(options) {
state.promptKeyOptions = options;
return {
profile: options.profile,
async retire(request) {
state.promptKeyRequest = request;
return {
status: 'completed',
keyId: request.keyId,
retirementId: request.retirementId,
preparationDigest: 'e'.repeat(64),
completionDigest: 'f'.repeat(64),
completedAtMs: 500,
};
},
async close() {
state.promptKeyClosed = true;
},
};
},
};
}
test('runs acknowledgement compaction from one private durable command file', async (t) => {
const state = {};
const command = acknowledgementCommand();
const result = await createLocalOwnerGcCommandRunner(dependencies(state)).run(
privateCommand(t, command),
);
assert.deepEqual(result, {
schemaVersion: 1,
operation: command.operation,
status: 'inserted',
gcMutationId: command.request.mutationId,
acknowledgementMutationId: command.request.acknowledgementMutationId,
acknowledgementKind: 'credential',
retentionEligibleAtMs: 200,
compactedAtMs: 300,
});
assert.deepEqual(state.ackOptions, command.options);
assert.deepEqual(state.ackRequest, command.request);
assert.equal(state.ackClosed, true);
});
test('runs pepper material collection without exposing destruction digests', async (t) => {
const state = {};
const command = {
schemaVersion: 1,
operation: 'owner.pepper-material.collect',
options: {
databasePath: '/private/ql3.sqlite',
profile: 'standalone',
keyringDirectory: '/private/keyring',
backupDirectory: '/backup/keyring',
retentionPolicy: {
version: 1,
acknowledgementRetentionMs: 604_800_000,
auditRetentionMs: 2_592_000_000,
backupRetentionMs: 2_592_000_000,
},
},
request: {
prepareMutationId: '00000000-0000-4000-8000-000000000b01',
prepareRequestId: 'gc-b01',
completeMutationId: '00000000-0000-4000-8000-000000000b02',
completeRequestId: 'gc-b02',
pepperKeyId: 'retired-v1',
},
};
const result = await createLocalOwnerGcCommandRunner(dependencies(state)).run(
privateCommand(t, command),
);
assert.deepEqual(result, {
schemaVersion: 1,
operation: command.operation,
status: 'existing',
prepareMutationId: command.request.prepareMutationId,
completeMutationId: command.request.completeMutationId,
pepperKeyId: command.request.pepperKeyId,
state: 'completed',
completedAtMs: 400,
});
assert.equal('destructionProofDigest' in result, false);
assert.equal(state.pepperClosed, true);
});
test('runs one bounded Prompt output collection without returning policy data', async (t) => {
const state = {};
const policy = { revision: 'retention-v1', retentionMs: 3_600_000 };
const command = {
schemaVersion: 1,
operation: 'owner.prompt-output.collect',
options: {
databasePath: '/private/ql3.sqlite',
profile: 'edge',
limit: 4,
retentionPolicyCatalog: {
schemaVersion: 1,
policies: [
{
projectId: 'project-a',
policy,
policyDigest:
pluginPackagePromptOutputArtifactRetentionPolicyDigest(policy),
},
],
},
},
request: {},
};
const result = await createLocalOwnerGcCommandRunner(dependencies(state)).run(
privateCommand(t, command),
);
assert.deepEqual(result, {
schemaVersion: 1,
operation: command.operation,
scanned: 3,
tombstoned: 2,
skipped: 1,
hasMore: false,
});
assert.deepEqual(state.promptOptions, command.options);
assert.equal(state.promptCollected, true);
assert.equal(state.promptClosed, true);
assert.equal('retentionPolicyCatalog' in result, false);
});
test('retires one Prompt output key without returning key material', async (t) => {
const state = {};
const command = {
schemaVersion: 1,
operation: 'owner.prompt-output-key.retire',
options: {
databasePath: '/private/ql3.sqlite',
profile: 'edge',
keyringPath: '/private/prompt-output-keyring.json',
},
request: {
keyId: 'qlpo-retired',
retirementId: 'retirement-a',
requestId: 'request-a',
mutationId: 'mutation-a',
},
};
const result = await createLocalOwnerGcCommandRunner(dependencies(state)).run(
privateCommand(t, command),
);
assert.deepEqual(result, {
schemaVersion: 1,
operation: command.operation,
status: 'completed',
keyId: command.request.keyId,
retirementId: command.request.retirementId,
preparationDigest: 'e'.repeat(64),
completionDigest: 'f'.repeat(64),
completedAtMs: 500,
});
assert.deepEqual(state.promptKeyOptions, command.options);
assert.deepEqual(state.promptKeyRequest, command.request);
assert.equal(state.promptKeyClosed, true);
assert.equal(JSON.stringify(result).includes('material'), false);
});
test('rejects widened or non-private command files before opening authority', async (t) => {
const state = {};
const runner = createLocalOwnerGcCommandRunner(dependencies(state));
const widened = privateCommand(t, acknowledgementCommand({ now: 1 }));
await assert.rejects(runner.run(widened), LocalOwnerGcCliConfigurationError);
const broad = privateCommand(t, acknowledgementCommand());
fs.chmodSync(broad, 0o644);
await assert.rejects(runner.run(broad), LocalOwnerGcCliConfigurationError);
assert.equal(state.ackOptions, undefined);
assert.equal(state.pepperOptions, undefined);
assert.equal(state.promptOptions, undefined);
});
test('binary has a bounded command-file-only interface', () => {
const help = spawnSync(
process.execPath,
[path.join(__dirname, '../dist/cli.js'), '--help'],
{
encoding: 'utf8',
},
);
assert.equal(help.status, 0);
assert.match(help.stdout, /^Usage: ql3-owner-gc run --command-file /);
assert.equal(help.stderr, '');
const invalid = spawnSync(
process.execPath,
[path.join(__dirname, '../dist/cli.js'), 'run'],
{
encoding: 'utf8',
},
);
assert.equal(invalid.status, 64);
assert.equal(invalid.stdout, '');
assert.equal(
JSON.parse(invalid.stderr).code,
'LOCAL_OWNER_GC_CLI_USAGE_INVALID',
);
});
@@ -0,0 +1,293 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { DatabaseSync } = require('node:sqlite');
const { test } = require('node:test');
const {
LocalOwnerPepperKeyringFileProvider,
localOwnerPepperKeyPath,
provisionLocalOwnerPepperKey,
} = require('@qinglong/local-owner-console/pepper-custody');
const {
destroyLocalOwnerPepperKey,
} = require('@qinglong/local-owner-console/pepper-custody/destructive');
const {
openLocalSqlitePepperGcDatabase,
} = require('@qinglong/local-sqlite/pepper-gc');
const { migrateLocalSqlitePath } = require('@qinglong/local-sqlite/migration');
const {
MIN_LOCAL_OWNER_PEPPER_ACK_RETENTION_MS,
MIN_LOCAL_OWNER_PEPPER_AUDIT_RETENTION_MS,
MIN_LOCAL_OWNER_PEPPER_BACKUP_RETENTION_MS,
} = require('@qinglong/runtime-core/local-owner-pepper-material-gc');
const {
LocalOwnerPepperMaterialGcMaterialUnavailableError,
openLocalOwnerPepperMaterialGc,
} = require('../dist/security-maintenance/pepperGc');
const RETIRED_KEY_ID = 'owner-key-retired';
const ACTIVE_KEY_ID = 'owner-key-active';
const REQUESTED_AT_MS = 3_000_000_000;
function policy() {
return {
version: 1,
acknowledgementRetentionMs: MIN_LOCAL_OWNER_PEPPER_ACK_RETENTION_MS,
auditRetentionMs: MIN_LOCAL_OWNER_PEPPER_AUDIT_RETENTION_MS,
backupRetentionMs: MIN_LOCAL_OWNER_PEPPER_BACKUP_RETENTION_MS,
};
}
function request() {
return {
prepareMutationId: '00000000-0000-4000-8000-000000000801',
prepareRequestId: 'pepper-gc-prepare',
completeMutationId: '00000000-0000-4000-8000-000000000802',
completeRequestId: 'pepper-gc-complete',
pepperKeyId: RETIRED_KEY_ID,
};
}
function audit(eventId, requestId, operation) {
return {
eventId,
requestId,
operationId: `owner.pepper.material_gc.${operation}`,
projectId: null,
subject: { type: 'system', id: 'owner-pepper-gc' },
authenticationId: 'local-owner-console',
outcome: 'allowed',
reasons: ['pepper_material_gc'],
fence: null,
occurredAtMs: REQUESTED_AT_MS,
};
}
async function fixture(t) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-pepper-gc-e2e-'));
const keyringDirectory = path.join(root, 'keyring');
const backupDirectory = path.join(root, 'backup');
const databasePath = path.join(root, 'qinglong3.sqlite');
fs.mkdirSync(keyringDirectory, { mode: 0o700 });
fs.mkdirSync(backupDirectory, { mode: 0o700 });
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const retiredRuntime = provisionLocalOwnerPepperKey({
keyringDirectory,
pepperKeyId: RETIRED_KEY_ID,
randomBytes: () => Buffer.alloc(32, 41),
});
const retiredBackup = provisionLocalOwnerPepperKey({
keyringDirectory: backupDirectory,
pepperKeyId: RETIRED_KEY_ID,
randomBytes: () => Buffer.alloc(32, 41),
});
const activeRuntime = provisionLocalOwnerPepperKey({
keyringDirectory,
pepperKeyId: ACTIVE_KEY_ID,
randomBytes: () => Buffer.alloc(32, 43),
});
const activeBackup = provisionLocalOwnerPepperKey({
keyringDirectory: backupDirectory,
pepperKeyId: ACTIVE_KEY_ID,
randomBytes: () => Buffer.alloc(32, 43),
});
await migrateLocalSqlitePath({ databasePath, profile: 'edge' });
const client = new DatabaseSync(databasePath);
client.exec('PRAGMA foreign_keys = ON');
client
.prepare(
`INSERT INTO "QingLong3LocalOwnerPepperKeys" (
pepper_key_id, material_digest, backup_digest, state, version,
register_mutation_id, activate_mutation_id, retire_mutation_id,
registered_at_ms, activated_at_ms, retired_at_ms
) VALUES (?, ?, ?, 'retired', 3, ?, ?, ?, 10, 50, 100)`,
)
.run(
RETIRED_KEY_ID,
retiredRuntime.digest,
retiredBackup.digest,
'00000000-0000-4000-8000-000000000811',
'00000000-0000-4000-8000-000000000812',
'00000000-0000-4000-8000-000000000813',
);
client
.prepare(
`INSERT INTO "QingLong3LocalOwnerPepperKeys" (
pepper_key_id, material_digest, backup_digest, state, version,
register_mutation_id, activate_mutation_id, registered_at_ms,
activated_at_ms
) VALUES (?, ?, ?, 'active', 2, ?, ?, 60, 100)`,
)
.run(
ACTIVE_KEY_ID,
activeRuntime.digest,
activeBackup.digest,
'00000000-0000-4000-8000-000000000821',
'00000000-0000-4000-8000-000000000822',
);
client
.prepare(
`INSERT INTO "QingLong3LocalOwnerPepperActivations" (
generation, mutation_id, expected_generation,
previous_pepper_key_id, active_pepper_key_id,
material_digest, backup_digest, activated_at_ms
) VALUES (1, ?, 0, NULL, ?, ?, ?, 50)`,
)
.run(
'00000000-0000-4000-8000-000000000812',
RETIRED_KEY_ID,
retiredRuntime.digest,
retiredBackup.digest,
);
client
.prepare(
`INSERT INTO "QingLong3LocalOwnerPepperActivations" (
generation, mutation_id, expected_generation,
previous_pepper_key_id, active_pepper_key_id,
material_digest, backup_digest, activated_at_ms
) VALUES (2, ?, 1, ?, ?, ?, ?, 100)`,
)
.run(
'00000000-0000-4000-8000-000000000822',
RETIRED_KEY_ID,
ACTIVE_KEY_ID,
activeRuntime.digest,
activeBackup.digest,
);
client.close();
return {
databasePath,
keyringDirectory,
backupDirectory,
retiredRuntime,
retiredBackup,
activeRuntime,
};
}
function openOptions(fixture) {
return {
databasePath: fixture.databasePath,
profile: 'edge',
keyringDirectory: fixture.keyringDirectory,
backupDirectory: fixture.backupDirectory,
retentionPolicy: policy(),
};
}
test('destroys runtime and backup material then replays from durable absence', async (t) => {
const prepared = await fixture(t);
const authority = await openLocalOwnerPepperMaterialGc(openOptions(prepared));
t.after(() => authority.close());
const first = await authority.collect(request());
assert.equal(first.status, 'inserted');
assert.equal(first.record.state, 'completed');
assert.equal(first.runtimeMaterial.status, 'destroyed');
assert.equal(first.backupMaterial.status, 'destroyed');
assert.equal(
fs.existsSync(
localOwnerPepperKeyPath(prepared.keyringDirectory, RETIRED_KEY_ID),
),
false,
);
assert.equal(
fs.existsSync(
localOwnerPepperKeyPath(prepared.backupDirectory, RETIRED_KEY_ID),
),
false,
);
assert.ok(
new LocalOwnerPepperKeyringFileProvider(prepared.keyringDirectory).resolve(
ACTIVE_KEY_ID,
),
);
const replay = await authority.collect(request());
assert.equal(replay.status, 'existing');
assert.equal(replay.runtimeMaterial.status, 'absent');
assert.equal(replay.backupMaterial.status, 'absent');
assert.equal(
replay.record.destructionProofDigest,
first.record.destructionProofDigest,
);
});
test('recovers after runtime deletion but before backup deletion and completion', async (t) => {
const prepared = await fixture(t);
const command = request();
const database = await openLocalSqlitePepperGcDatabase({
databasePath: prepared.databasePath,
profile: 'edge',
});
await database.materialGc.prepare({
mutationId: command.prepareMutationId,
requestId: command.prepareRequestId,
pepperKeyId: command.pepperKeyId,
expectedMaterialDigest: prepared.retiredRuntime.digest,
expectedBackupMaterialDigest: prepared.retiredBackup.digest,
expectedActivePepperKeyId: ACTIVE_KEY_ID,
expectedActiveGeneration: 2,
expectedActiveMaterialDigest: prepared.activeRuntime.digest,
retentionPolicy: policy(),
preparedAtMs: REQUESTED_AT_MS,
audit: audit(
command.prepareMutationId,
command.prepareRequestId,
'prepare',
),
});
await database.close();
destroyLocalOwnerPepperKey({
keyringDirectory: prepared.keyringDirectory,
pepperKeyId: RETIRED_KEY_ID,
materialRole: 'runtime',
expectedMaterialDigest: prepared.retiredRuntime.digest,
prepareMutationId: command.prepareMutationId,
});
const authority = await openLocalOwnerPepperMaterialGc(openOptions(prepared));
t.after(() => authority.close());
const recovered = await authority.collect(command);
assert.equal(recovered.record.state, 'completed');
assert.equal(recovered.runtimeMaterial.status, 'absent');
assert.equal(recovered.backupMaterial.status, 'destroyed');
});
test('fails before prepare when the active independent backup is missing', async (t) => {
const prepared = await fixture(t);
fs.unlinkSync(
localOwnerPepperKeyPath(prepared.backupDirectory, ACTIVE_KEY_ID),
);
const authority = await openLocalOwnerPepperMaterialGc(openOptions(prepared));
t.after(() => authority.close());
await assert.rejects(
authority.collect(request()),
LocalOwnerPepperMaterialGcMaterialUnavailableError,
);
assert.equal(
fs.existsSync(
localOwnerPepperKeyPath(prepared.keyringDirectory, RETIRED_KEY_ID),
),
true,
);
});
test('rejects caller-controlled time before opening a destructive operation', async (t) => {
const prepared = await fixture(t);
const authority = await openLocalOwnerPepperMaterialGc(openOptions(prepared));
t.after(() => authority.close());
await assert.rejects(
authority.collect({
...request(),
requestedAtMs: Number.MAX_SAFE_INTEGER,
}),
/request shape is invalid/,
);
assert.equal(
fs.existsSync(
localOwnerPepperKeyPath(prepared.keyringDirectory, RETIRED_KEY_ID),
),
true,
);
});
@@ -0,0 +1,89 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { DatabaseSync } = require('node:sqlite');
const test = require('node:test');
const {
PluginPackagePromptOutputFileKeyring,
provisionPluginPackagePromptOutputFileKeyring,
rotatePluginPackagePromptOutputFileKeyring,
} = require('../../ql3-ai/dist/prompt-output/key-management/pluginPackagePromptOutputFileKeyring.js');
const {
setupScenario,
} = require('../../ql3-ai/test/fixtures/pluginPackagePromptCrashFixture.cjs');
const {
openLocalOwnerPromptOutputKeyRetirement,
} = require('../dist/prompt-output-maintenance/promptOutputKeyRetirement.js');
test('Local Owner retires one inactive Prompt output key and exactly replays', async (t) => {
const directory = fs.mkdtempSync(
path.join(os.tmpdir(), 'ql3-owner-output-key-retirement-'),
);
fs.chmodSync(directory, 0o700);
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
const databasePath = path.join(directory, 'runtime.sqlite');
const keyringPath = path.join(directory, 'prompt-output-keyring.json');
await setupScenario({
databasePath,
statePath: path.join(directory, 'fixture-state.json'),
profile: 'edge',
operation: 'admission',
});
const provisioned =
await provisionPluginPackagePromptOutputFileKeyring(keyringPath);
await rotatePluginPackagePromptOutputFileKeyring({
filePath: keyringPath,
expectedActiveKeyId: provisioned.activeKeyId,
expectedCatalogDigest: provisioned.catalogDigest,
});
const authority = await openLocalOwnerPromptOutputKeyRetirement({
databasePath,
profile: 'edge',
keyringPath,
});
t.after(() => authority.close());
const request = {
keyId: provisioned.activeKeyId,
retirementId: 'owner-retirement-a',
requestId: 'owner-retirement-request-a',
mutationId: 'owner-retirement-mutation-a',
};
const retired = await authority.retire(request);
assert.equal(retired.status, 'completed');
assert.equal((await authority.retire(request)).status, 'existing');
assert.equal(retired.keyId, provisioned.activeKeyId);
assert.equal(
(await new PluginPackagePromptOutputFileKeyring(keyringPath).inspect(
provisioned.activeKeyId,
)).state,
'absent',
);
await authority.close();
const client = new DatabaseSync(databasePath, { readOnly: true });
try {
assert.equal(
client
.prepare(
`SELECT count(*) AS count
FROM "ModelInvocationPromptOutputKeyRetirementPreparations"`,
)
.get().count,
1,
);
assert.equal(
client
.prepare(
`SELECT count(*) AS count
FROM "ModelInvocationPromptOutputKeyRetirementCompletions"`,
)
.get().count,
1,
);
} finally {
client.close();
}
});