feat(ql3): gate primary on shadow capture evidence

This commit is contained in:
whyour
2026-08-19 03:27:32 +08:00
parent c60a86d1bf
commit be98d6753b
28 changed files with 2227 additions and 30 deletions
+151
View File
@@ -0,0 +1,151 @@
#!/usr/bin/env node
require('ts-node/register/transpile-only');
const fs = require('node:fs');
const path = require('node:path');
const {
createLegacyShadowPrimaryGateReceipt,
} = require('../back/runtime/domain/legacyShadowPrimaryGate');
const MAX_EVIDENCE_BYTES = 1024 * 1024;
function parseArguments(argv) {
const options = { profile: 'edge' };
for (const argument of argv) {
if (argument === '--') continue;
if (argument.startsWith('--profile=')) {
options.profile = argument.slice('--profile='.length);
} else if (argument.startsWith('--capture=')) {
options.capturePath = path.resolve(argument.slice('--capture='.length));
} else if (argument.startsWith('--terminal=')) {
options.terminalPath = path.resolve(argument.slice('--terminal='.length));
} else if (argument.startsWith('--resource=')) {
options.resourcePath = path.resolve(argument.slice('--resource='.length));
} else if (argument.startsWith('--output=')) {
options.outputPath = path.resolve(argument.slice('--output='.length));
} else if (argument.startsWith('--generated-at-ms=')) {
const raw = argument.slice('--generated-at-ms='.length);
if (!/^\d+$/u.test(raw)) {
throw new TypeError('--generated-at-ms must be an integer');
}
options.generatedAtMs = Number(raw);
} else {
throw new TypeError(`Unsupported argument: ${argument}`);
}
}
if (options.profile !== 'edge' && options.profile !== 'standalone') {
throw new TypeError('--profile must be edge or standalone');
}
for (const name of [
'capturePath',
'terminalPath',
'resourcePath',
'outputPath',
]) {
if (!options[name] || !path.isAbsolute(options[name])) {
throw new TypeError(`--${name.replace('Path', '')} is required`);
}
}
if (
options.generatedAtMs !== undefined &&
(!Number.isSafeInteger(options.generatedAtMs) || options.generatedAtMs < 0)
) {
throw new TypeError('--generated-at-ms is invalid');
}
return options;
}
function readEvidence(sourcePath) {
const descriptor = fs.openSync(
sourcePath,
fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0),
);
try {
const stat = fs.fstatSync(descriptor);
if (
!stat.isFile() ||
(stat.mode & 0o077) !== 0 ||
stat.size < 2 ||
stat.size > MAX_EVIDENCE_BYTES
) {
throw new TypeError('Primary gate evidence file shape is invalid');
}
const bytes = Buffer.alloc(stat.size);
let offset = 0;
while (offset < bytes.length) {
const count = fs.readSync(
descriptor,
bytes,
offset,
bytes.length - offset,
offset,
);
if (count === 0) throw new Error('Primary gate evidence read stalled');
offset += count;
}
return {
value: JSON.parse(bytes.toString('utf8')),
};
} finally {
fs.closeSync(descriptor);
}
}
function writeReceipt(outputPath, receipt) {
const descriptor = fs.openSync(outputPath, 'wx', 0o600);
try {
fs.writeFileSync(descriptor, `${JSON.stringify(receipt)}\n`, 'utf8');
fs.fsyncSync(descriptor);
} finally {
fs.closeSync(descriptor);
}
}
function run(options) {
const capture = readEvidence(options.capturePath);
const terminal = readEvidence(options.terminalPath);
const resource = readEvidence(options.resourcePath);
const receipt = createLegacyShadowPrimaryGateReceipt({
profile: options.profile,
generatedAtMs: options.generatedAtMs ?? Date.now(),
capture: capture.value,
terminal: terminal.value,
resource: resource.value,
});
if (receipt.assessment !== 'eligible') {
const error = new Error(
`Legacy Shadow Primary gate is ineligible: ${receipt.violations.join(
',',
)}`,
);
error.code = 'QL3_PRIMARY_GATE_INELIGIBLE';
throw error;
}
writeReceipt(options.outputPath, receipt);
return receipt;
}
function main() {
const receipt = run(parseArguments(process.argv.slice(2)));
process.stdout.write(`${JSON.stringify(receipt)}\n`);
}
module.exports = {
MAX_EVIDENCE_BYTES,
parseArguments,
readEvidence,
run,
writeReceipt,
};
if (require.main === module) {
try {
main();
} catch (error) {
process.stderr.write(
`${error instanceof Error ? error.message : String(error)}\n`,
);
process.exitCode = 1;
}
}
@@ -369,7 +369,7 @@ async function runSummary(database) {
});
}
async function rollbackChild(mode, expectedBefore) {
async function rollbackChild(mode, expectedBefore, profile) {
const databasePath = process.env.QL3_SHADOW_DRILL_DATABASE;
if (!path.isAbsolute(databasePath ?? '')) {
throw new QingLong3LegacyShadowResourceRollbackError(
@@ -380,10 +380,17 @@ async function rollbackChild(mode, expectedBefore) {
await taskLimit.setCustomLimit();
const ScheduleService = fromRuntime('services/schedule').default;
const bridge = fromRuntime('runtime/compatibility/legacyExecutionBridge');
const { createLegacyShadowCaptureReport } = fromRuntime(
'runtime/application/legacyShadowCaptureAuthority',
);
const { sequelize } = fromRuntime('data');
let shortCircuitFactCalls = 0;
try {
const configuredOrigins = bridge.configuredLegacyShadowOrigins();
const captureBefore =
mode === 'enabled'
? bridge.legacyShadowCaptureSnapshot(['system'])
: undefined;
if (mode === 'off') {
const observation = bridge.observeLegacyExecution('system', () => {
shortCircuitFactCalls += 1;
@@ -434,6 +441,15 @@ async function rollbackChild(mode, expectedBefore) {
`${mode} restart did not preserve the expected Legacy result`,
);
}
const capture =
mode === 'enabled'
? createLegacyShadowCaptureReport(
profile,
['system'],
captureBefore,
bridge.legacyShadowCaptureSnapshot(['system']),
)
: undefined;
return Object.freeze({
mode,
configuredOrigins,
@@ -445,6 +461,7 @@ async function rollbackChild(mode, expectedBefore) {
shortCircuitFactCalls,
defaultObserverLoaded,
repositoryLoaded,
...(capture === undefined ? {} : { capture }),
peakProcessRssBytes: process.resourceUsage().maxRSS * 1024,
});
} finally {
@@ -558,6 +575,7 @@ async function runEvidence(options) {
[
'--internal-child=rollback-enabled',
`--expected-before=${fixtureRunCount}`,
`--profile=${options.profile}`,
],
{ ...childEnvironment, QL3_SHADOW_ORIGINS: 'system' },
)
@@ -568,6 +586,7 @@ async function runEvidence(options) {
[
'--internal-child=rollback-off',
`--expected-before=${fixtureRunCount + 1}`,
`--profile=${options.profile}`,
],
{ ...childEnvironment, QL3_SHADOW_ORIGINS: '' },
)
@@ -653,6 +672,7 @@ async function runEvidence(options) {
runDelta: enabled.runDelta,
defaultObserverLoaded: enabled.defaultObserverLoaded,
repositoryLoaded: enabled.repositoryLoaded,
capture: enabled.capture,
peakProcessRssBytes: enabled.peakProcessRssBytes,
}),
off: Object.freeze({
@@ -729,6 +749,7 @@ async function main() {
report = await rollbackChild(
mode === 'rollback-enabled' ? 'enabled' : 'off',
expectedBefore,
profile,
);
} else {
throw new QingLong3LegacyShadowResourceRollbackError(