Files
qinglong/scripts/ql3-physical-edge-compose-storage.cjs
T

1403 lines
42 KiB
JavaScript

#!/usr/bin/env node
'use strict';
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
const { DatabaseSync } = require('node:sqlite');
const {
canonicalDigest,
collectObservedPlatform,
mountForPath,
parseMountTable,
} = require('./ql3-physical-edge-evidence.cjs');
const MIB = 1024 * 1024;
const MAX_INPUT_BYTES = 256 * 1024;
const MAX_TREE_ENTRIES = 512;
const SECTOR_BYTES = 512;
const MANIFEST_KEYS = Object.freeze([
'databasePayloadBytes',
'deviceId',
'evidenceClass',
'expectedArchitecture',
'expectedFilesystem',
'maximumPrepareWriteAmplificationPermille',
'maximumResumeWriteAmplificationPermille',
'profile',
'schemaVersion',
'snapshotCount',
]);
const SESSION_KEYS = Object.freeze([
'barrier',
'collection',
'dataPath',
'deploymentRoot',
'evidenceClass',
'manifestDigest',
'prepareMeasurement',
'preparedAt',
'schemaVersion',
'sessionId',
'sha256',
'uid',
]);
const STORAGE_IDENTITY_KEYS = Object.freeze([
'architecture',
'blockDevice',
'bootId',
'filesystem',
'mountOptions',
'mountPath',
'platform',
'sectorsWritten',
]);
const TREE_KEYS = Object.freeze([
'allocatedBytes',
'directories',
'entries',
'logicalBytes',
'regularFiles',
]);
const SNAPSHOT_KEYS = Object.freeze([
'bytes',
'contractVersion',
'pageCount',
'pageSize',
'sha256',
]);
const REPORT_KEYS = Object.freeze([
'evidenceClass',
'generatedAt',
'manifest',
'measurements',
'observed',
'outcomes',
'qualification',
'schemaVersion',
'session',
'sha256',
'supported',
]);
const REPORT_SESSION_KEYS = Object.freeze([
'preparedAt',
'sessionDigest',
'sessionId',
'targetRolloutId',
'targetSnapshot',
]);
const REPORT_OBSERVED_KEYS = Object.freeze(['after', 'afterStart', 'before']);
const REPORT_MEASUREMENT_KEYS = Object.freeze([
'barrierAllocatedBytes',
'collectedSnapshotBytes',
'finalAllocatedBytes',
'logicalSnapshotBytes',
'prepareDeviceBytesWritten',
'prepareWriteAmplificationPermille',
'reclaimedAllocatedBytes',
'resumeDeviceBytesWritten',
'resumeWriteAmplificationPermille',
]);
const REPORT_OUTCOME_KEYS = Object.freeze([
'commitStatus',
'replayStatus',
'retainedSnapshots',
'sqliteIntegrity',
'stageRemoved',
'tombstonePresent',
]);
const REPORT_QUALIFICATION_KEYS = Object.freeze([
'doesNotProve',
'measures',
'passed',
'violations',
]);
const MEASURES = Object.freeze([
'production_collection_prepare',
'durable_collection_rename_stage',
'boot_identity_change',
'production_collection_commit_recovery',
'exact_collection_commit_replay',
'sqlite_integrity_after_reboot',
'per_boot_partition_write_sectors',
'allocated_snapshot_space_reclamation',
]);
const EXCLUSIONS = Object.freeze([
'abrupt_power_interruption_provenance',
'main_deployment_filesystem',
'whole_device_flash_translation_layer_write_amplification',
'mtd_ubi_device_write_amplification',
'long_term_flash_endurance',
'compose_restore_replacement_power_loss',
'standalone_or_cluster_profile',
]);
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 SHA256_PATTERN = /^[0-9a-f]{64}$/;
class QingLong3PhysicalComposeStorageEvidenceError extends Error {
constructor(message) {
super(
`QingLong 3.0 physical Edge Compose storage evidence failed: ${message}`,
);
this.name = 'QingLong3PhysicalComposeStorageEvidenceError';
}
}
function exactKeys(value, expected, label) {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${label} must be an object`,
);
}
if (JSON.stringify(Object.keys(value).sort()) !== JSON.stringify(expected)) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${label} keys must be exactly ${expected.join(', ')}`,
);
}
}
function hasExactKeys(value, expected) {
return (
value !== null &&
typeof value === 'object' &&
!Array.isArray(value) &&
JSON.stringify(Object.keys(value).sort()) === JSON.stringify(expected)
);
}
function normalizeComposeStorageManifest(value) {
exactKeys(value, MANIFEST_KEYS, 'manifest');
if (
value.schemaVersion !== 1 ||
value.evidenceClass !== 'physical_edge_compose_storage_candidate' ||
value.profile !== 'edge'
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'manifest identity is invalid',
);
}
if (
typeof value.deviceId !== 'string' ||
!/^[a-z0-9][a-z0-9._-]{2,63}$/.test(value.deviceId)
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'manifest deviceId is invalid',
);
}
if (!['x64', 'arm64', 'arm'].includes(value.expectedArchitecture)) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'manifest expectedArchitecture is invalid',
);
}
if (
typeof value.expectedFilesystem !== 'string' ||
!/^[a-z0-9][a-z0-9._-]{1,31}$/.test(value.expectedFilesystem)
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'manifest expectedFilesystem is invalid',
);
}
if (value.snapshotCount !== 3) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'manifest snapshotCount must be 3',
);
}
if (
!Number.isSafeInteger(value.databasePayloadBytes) ||
value.databasePayloadBytes < MIB ||
value.databasePayloadBytes > 64 * MIB
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'manifest databasePayloadBytes must be between 1 and 64 MiB',
);
}
for (const name of [
'maximumPrepareWriteAmplificationPermille',
'maximumResumeWriteAmplificationPermille',
]) {
if (
!Number.isSafeInteger(value[name]) ||
value[name] < 1000 ||
value[name] > 1_000_000
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`manifest ${name} must be between 1000 and 1000000`,
);
}
}
return Object.freeze({
schemaVersion: 1,
evidenceClass: 'physical_edge_compose_storage_candidate',
profile: 'edge',
deviceId: value.deviceId,
expectedArchitecture: value.expectedArchitecture,
expectedFilesystem: value.expectedFilesystem,
snapshotCount: 3,
databasePayloadBytes: value.databasePayloadBytes,
maximumPrepareWriteAmplificationPermille:
value.maximumPrepareWriteAmplificationPermille,
maximumResumeWriteAmplificationPermille:
value.maximumResumeWriteAmplificationPermille,
});
}
function parseArguments(argv) {
const options = { json: false };
let separatorSeen = false;
let phase;
for (const argument of argv) {
if (argument === '--' && !separatorSeen) {
separatorSeen = true;
continue;
}
if (argument === '--json') {
options.json = true;
continue;
}
if (!phase && (argument === 'prepare' || argument === 'resume')) {
phase = argument;
continue;
}
const separator = argument.indexOf('=');
if (separator < 1) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`unsupported argument ${argument}`,
);
}
const name = argument.slice(0, separator);
const value = argument.slice(separator + 1);
if (name === '--manifest') options.manifestPath = value;
else if (name === '--data-path') options.dataPath = value;
else if (name === '--session') options.sessionPath = value;
else if (name === '--output') options.outputPath = value;
else {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`unsupported argument ${argument}`,
);
}
}
if (!phase) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'phase must be prepare or resume',
);
}
for (const name of ['manifestPath', 'sessionPath']) {
if (!path.isAbsolute(options[name] ?? '')) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${name} must be absolute`,
);
}
}
if (
(phase === 'prepare' && !path.isAbsolute(options.dataPath ?? '')) ||
(phase === 'resume' && options.dataPath !== undefined)
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'dataPath is required only for prepare and must be absolute',
);
}
if (
(phase === 'resume' && !path.isAbsolute(options.outputPath ?? '')) ||
(phase === 'prepare' && options.outputPath !== undefined)
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'outputPath is required only for resume and must be absolute',
);
}
return Object.freeze({ phase, ...options });
}
function readBoundedJson(filePath, label) {
let stat;
try {
stat = fs.lstatSync(filePath);
} catch (error) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${label} is unavailable: ${error.message}`,
);
}
const uid = process.geteuid?.();
if (
!Number.isSafeInteger(uid) ||
!stat.isFile() ||
stat.isSymbolicLink() ||
stat.uid !== uid ||
(stat.mode & 0o777) !== 0o600 ||
stat.nlink !== 1 ||
stat.size < 2 ||
stat.size > MAX_INPUT_BYTES
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${label} must be a bounded current-user 0600 single-link file`,
);
}
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${label} is invalid: ${error.message}`,
);
}
}
function readManifest(filePath) {
return normalizeComposeStorageManifest(readBoundedJson(filePath, 'manifest'));
}
function assertPrivateDirectory(directory, label) {
const stat = fs.lstatSync(directory);
const uid = process.geteuid?.();
if (
!Number.isSafeInteger(uid) ||
!stat.isDirectory() ||
stat.isSymbolicLink() ||
stat.uid !== uid ||
(stat.mode & 0o777) !== 0o700 ||
fs.realpathSync(directory) !== directory
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${label} must be a canonical current-user 0700 directory`,
);
}
}
function assertChildPath(parent, candidate, label) {
const normalized = path.normalize(candidate);
if (
normalized !== candidate ||
candidate === parent ||
!candidate.startsWith(`${parent}${path.sep}`)
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`${label} must remain inside dataPath`,
);
}
}
function writePrivateNoReplace(filePath, contents) {
const parent = fs.realpathSync(path.dirname(filePath));
assertPrivateDirectory(parent, 'output parent');
const resolved = path.join(parent, path.basename(filePath));
if (resolved !== filePath) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'output path must be canonical',
);
}
const descriptor = fs.openSync(filePath, 'wx', 0o600);
try {
fs.writeFileSync(descriptor, contents, 'utf8');
fs.fsyncSync(descriptor);
} finally {
fs.closeSync(descriptor);
}
const directoryDescriptor = fs.openSync(parent, 'r');
try {
fs.fsyncSync(directoryDescriptor);
} finally {
fs.closeSync(directoryDescriptor);
}
}
function syncDirectory(directory) {
const descriptor = fs.openSync(directory, 'r');
try {
fs.fsyncSync(descriptor);
} finally {
fs.closeSync(descriptor);
}
}
function collectTreeStorage(root) {
let entries = 0;
let regularFiles = 0;
let directories = 0;
let logicalBytes = 0;
let allocatedBytes = 0;
const visit = (target) => {
entries += 1;
if (entries > MAX_TREE_ENTRIES) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'deployment tree exceeded 512 entries',
);
}
const stat = fs.lstatSync(target);
if (stat.isSymbolicLink()) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'deployment tree contains a symlink',
);
}
if (stat.isDirectory()) {
directories += 1;
for (const entry of fs.readdirSync(target).sort()) {
visit(path.join(target, entry));
}
return;
}
if (!stat.isFile()) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'deployment tree contains a non-regular entry',
);
}
regularFiles += 1;
logicalBytes += stat.size;
allocatedBytes += stat.blocks * SECTOR_BYTES;
};
visit(root);
return Object.freeze({
entries,
regularFiles,
directories,
logicalBytes,
allocatedBytes,
});
}
function parseSectorsWritten(raw) {
const fields = raw.trim().split(/\s+/);
const value = Number(fields[6]);
if (fields.length < 11 || !Number.isSafeInteger(value) || value < 0) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'block device statistics are invalid',
);
}
return value;
}
function collectStorageIdentity(dataPath) {
if (process.platform !== 'linux') {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'collector requires Linux',
);
}
const observed = collectObservedPlatform(dataPath);
const mounts = parseMountTable(fs.readFileSync('/proc/mounts', 'utf8'));
const mount = mountForPath(mounts, dataPath);
if (!mount || mount.path !== dataPath) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'dataPath must be a dedicated mount point',
);
}
if (!mount.source.startsWith('/dev/')) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'dataPath must use a directly observable block device',
);
}
const blockDevice = fs.realpathSync(mount.source);
const blockName = path.basename(blockDevice);
const statPath = path.join('/sys/class/block', blockName, 'stat');
const sectorsWritten = parseSectorsWritten(fs.readFileSync(statPath, 'utf8'));
return Object.freeze({
platform: observed.platform,
architecture: observed.architecture,
bootId: observed.bootId,
mountPath: mount.path,
filesystem: mount.filesystem,
mountOptions: mount.options,
blockDevice,
sectorsWritten,
});
}
function validateStorageIdentity(manifest, identity, dataPath) {
const violations = [];
if (identity.platform !== 'linux') violations.push('platform must be Linux');
if (identity.architecture !== manifest.expectedArchitecture) {
violations.push('architecture did not match manifest');
}
if (identity.mountPath !== dataPath) {
violations.push('dataPath was not the dedicated mount point');
}
if (identity.filesystem !== manifest.expectedFilesystem) {
violations.push('filesystem did not match manifest');
}
if (!identity.mountOptions.includes('rw')) {
violations.push('dataPath was not mounted read-write');
}
if (!identity.blockDevice.startsWith('/dev/')) {
violations.push('block device identity was invalid');
}
return Object.freeze(violations);
}
function snapshotReceipt(evidence) {
return Object.freeze({
contractVersion: evidence.contractVersion,
sha256: evidence.sha256,
bytes: evidence.bytes,
pageCount: evidence.pageCount,
pageSize: evidence.pageSize,
});
}
function isSnapshot(value) {
return (
hasExactKeys(value, SNAPSHOT_KEYS) &&
value.contractVersion === 44 &&
typeof value.sha256 === 'string' &&
SHA256_PATTERN.test(value.sha256) &&
Number.isSafeInteger(value.bytes) &&
value.bytes > 0 &&
Number.isSafeInteger(value.pageCount) &&
value.pageCount > 0 &&
Number.isSafeInteger(value.pageSize) &&
value.pageSize >= 512 &&
value.pageSize <= 65_536
);
}
function rolloutReceipt(command, evidence, recordedAtMs) {
const result = Object.freeze({
schemaVersion: 1,
operation: 'local.deployment.compose.apply',
status: 'active',
attemptedGeneration: command.request.expectedGeneration,
activeGeneration: command.request.expectedGeneration,
profile: 'edge',
health: Object.freeze({ event: 'active' }),
service: Object.freeze({ kind: 'compose' }),
});
return Object.freeze({
schema: 'qinglong/local-compose-rollout-receipt@v2',
commandDigest: canonicalDigest(command),
rolloutId: command.request.rolloutId,
attemptedGeneration: command.request.expectedGeneration,
recordedAtMs,
healthEventDigest: canonicalDigest({
generation: command.request.expectedGeneration,
status: 'active',
}),
sqlite: Object.freeze({
contractVersion: evidence.contractVersion,
writeContractVersion: evidence.writeContractVersion,
writeObservation: 'unchanged',
backup: Object.freeze({
sha256: evidence.sha256,
bytes: evidence.bytes,
pageCount: evidence.pageCount,
pageSize: evidence.pageSize,
}),
}),
result,
});
}
function inflateDatabase(databasePath, payloadBytes) {
const database = new DatabaseSync(databasePath);
try {
database.exec(
'PRAGMA journal_mode = DELETE; CREATE TABLE ql3_physical_storage_payload (id INTEGER PRIMARY KEY, payload BLOB NOT NULL);',
);
const insert = database.prepare(
'INSERT INTO ql3_physical_storage_payload(payload) VALUES (zeroblob(?))',
);
const chunkBytes = Math.min(MIB, payloadBytes);
let remaining = payloadBytes;
while (remaining > 0) {
insert.run(Math.min(chunkBytes, remaining));
remaining -= Math.min(chunkBytes, remaining);
}
database.exec('DROP TABLE ql3_physical_storage_payload;');
} finally {
database.close();
}
}
function requireBuiltProducts() {
const root = path.resolve(__dirname, '..');
try {
return Object.freeze({
root,
deployment: require(path.join(
root,
'packages/ql3-local-owner-cli/dist/deployment/localDeployment.js',
)),
rolloutSafety: require(path.join(
root,
'packages/ql3-local-sqlite/dist/readiness/rolloutSafety.js',
)),
});
} catch (error) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`QingLong 3.0 packages must be built first: ${error.message}`,
);
}
}
function sessionContents(session) {
return `${JSON.stringify(session, null, 2)}\n`;
}
function normalizeSession(value) {
exactKeys(value, SESSION_KEYS, 'session');
const { sha256, ...body } = value;
const prepareCommand = value.collection?.prepareCommand;
const commitCommand = value.collection?.commitCommand;
const target = value.collection?.target;
const expectedDeploymentRoot = path.join(
value.dataPath ?? '',
`.ql3-compose-storage-${value.sessionId ?? ''}`,
);
const expectedTargetPath = path.join(
expectedDeploymentRoot,
'service',
'rollout-backups',
`${target?.rolloutId ?? ''}.sqlite`,
);
const expectedStagePath = path.join(
path.dirname(expectedTargetPath),
`.${path.basename(expectedTargetPath)}.ql3-collection-stage`,
);
if (
value.schemaVersion !== 1 ||
value.evidenceClass !== 'physical_edge_compose_storage_session' ||
!UUID_V4_PATTERN.test(value.sessionId ?? '') ||
!SHA256_PATTERN.test(value.manifestDigest ?? '') ||
!Number.isSafeInteger(value.uid) ||
value.uid < 0 ||
typeof value.preparedAt !== 'string' ||
!Number.isFinite(Date.parse(value.preparedAt)) ||
!path.isAbsolute(value.dataPath ?? '') ||
!path.isAbsolute(value.deploymentRoot ?? '') ||
!hasExactKeys(value.barrier?.identity, STORAGE_IDENTITY_KEYS) ||
!hasExactKeys(value.barrier?.tree, TREE_KEYS) ||
!Number.isSafeInteger(value.barrier?.sectorsWritten) ||
!hasExactKeys(value.prepareMeasurement, [
'deviceBytesWritten',
'logicalSnapshotBytes',
'writeAmplificationPermille',
]) ||
!hasExactKeys(value.collection, [
'commitCommand',
'prepareCommand',
'snapshotCount',
'stagePath',
'target',
'targetPath',
]) ||
value.collection.snapshotCount !== 3 ||
!hasExactKeys(value.collection.target, ['rolloutId', 'snapshot']) ||
!UUID_V4_PATTERN.test(value.collection.target.rolloutId ?? '') ||
!isSnapshot(value.collection.target.snapshot) ||
value.dataPath !== path.normalize(value.dataPath) ||
value.deploymentRoot !== expectedDeploymentRoot ||
value.barrier.identity.mountPath !== value.dataPath ||
value.barrier.sectorsWritten !== value.barrier.identity.sectorsWritten ||
TREE_KEYS.some(
(key) =>
!Number.isSafeInteger(value.barrier.tree[key]) ||
value.barrier.tree[key] < 0,
) ||
!Number.isSafeInteger(value.prepareMeasurement.logicalSnapshotBytes) ||
value.prepareMeasurement.logicalSnapshotBytes < 1 ||
!Number.isSafeInteger(value.prepareMeasurement.deviceBytesWritten) ||
value.prepareMeasurement.deviceBytesWritten < 0 ||
amplificationPermille(
value.prepareMeasurement.deviceBytesWritten,
value.prepareMeasurement.logicalSnapshotBytes,
) !== value.prepareMeasurement.writeAmplificationPermille ||
!hasExactKeys(prepareCommand, [
'operation',
'options',
'request',
'schemaVersion',
]) ||
prepareCommand.schemaVersion !== 1 ||
prepareCommand.operation !==
'local.deployment.compose.evidence-collection.prepare' ||
!hasExactKeys(prepareCommand.options, [
'allowRootService',
'deploymentRoot',
]) ||
prepareCommand.options.deploymentRoot !== value.deploymentRoot ||
prepareCommand.options.allowRootService !== (value.uid === 0) ||
!hasExactKeys(prepareCommand.request, [
'collectionId',
'expectedGeneration',
'preparedAtMs',
'restoreIds',
'rolloutIds',
]) ||
!UUID_V4_PATTERN.test(prepareCommand.request.collectionId ?? '') ||
prepareCommand.request.expectedGeneration !== 4 ||
!Number.isSafeInteger(prepareCommand.request.preparedAtMs) ||
JSON.stringify(prepareCommand.request.rolloutIds) !==
JSON.stringify([target.rolloutId]) ||
JSON.stringify(prepareCommand.request.restoreIds) !== '[]' ||
!hasExactKeys(commitCommand, [
'operation',
'options',
'request',
'schemaVersion',
]) ||
commitCommand.schemaVersion !== 1 ||
commitCommand.operation !==
'local.deployment.compose.evidence-collection.commit' ||
JSON.stringify(commitCommand.options) !==
JSON.stringify(prepareCommand.options) ||
!hasExactKeys(commitCommand.request, [
'collectionId',
'committedAtMs',
'expectedGeneration',
]) ||
commitCommand.request.collectionId !==
prepareCommand.request.collectionId ||
commitCommand.request.expectedGeneration !==
prepareCommand.request.expectedGeneration ||
!Number.isSafeInteger(commitCommand.request.committedAtMs) ||
commitCommand.request.committedAtMs < prepareCommand.request.preparedAtMs ||
value.collection.targetPath !== expectedTargetPath ||
value.collection.stagePath !== expectedStagePath ||
value.sha256 !== canonicalDigest(body)
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'session is invalid or drifted',
);
}
return Object.freeze(value);
}
async function preparePhase(options, manifest) {
const dataPath = fs.realpathSync(options.dataPath);
assertPrivateDirectory(dataPath, 'dataPath');
assertChildPath(dataPath, options.sessionPath, 'sessionPath');
if (fs.existsSync(options.sessionPath)) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'sessionPath already exists',
);
}
const initialIdentity = collectStorageIdentity(dataPath);
const preflightViolations = validateStorageIdentity(
manifest,
initialIdentity,
dataPath,
);
if (preflightViolations.length > 0) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`device preflight rejected: ${preflightViolations.join('; ')}`,
);
}
const products = requireBuiltProducts();
const sessionId = crypto.randomUUID();
const deploymentRoot = path.join(
dataPath,
`.ql3-compose-storage-${sessionId}`,
);
const uid = process.getuid?.();
if (!Number.isSafeInteger(uid)) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'a stable POSIX UID is required',
);
}
const timestamp = Date.now();
const image = `physical.invalid/qinglong3-local@sha256:${'a'.repeat(64)}`;
await products.deployment.prepareLocalDeployment({
schemaVersion: 1,
operation: 'local.deployment.prepare',
options: {
deploymentRoot,
profile: 'edge',
instanceId: `physical-compose-${sessionId.slice(0, 8)}`,
busyTimeoutMs: 100,
service: {
kind: 'compose',
image,
allowRootService: uid === 0,
},
},
request: {
ownerPepperKeyId: 'owner-v1',
registerMutationId: crypto.randomUUID(),
activateMutationId: crypto.randomUUID(),
registeredAtMs: timestamp,
activatedAtMs: timestamp + 1,
},
});
const databasePath = path.join(deploymentRoot, 'qinglong3.sqlite');
inflateDatabase(databasePath, manifest.databasePayloadBytes);
const rolloutIds = [];
const snapshots = [];
for (let generation = 2; generation <= 4; generation += 1) {
await products.deployment.switchLocalDeploymentComposeRevision({
schemaVersion: 1,
operation: 'local.deployment.compose.upgrade',
options: {
deploymentRoot,
allowRootService: uid === 0,
},
request: {
expectedGeneration: generation - 1,
image,
mutationId: crypto.randomUUID(),
changedAtMs: timestamp + generation,
},
});
const rolloutId = crypto.randomUUID();
const command = {
schemaVersion: 1,
operation: 'local.deployment.compose.apply',
options: {
deploymentRoot,
dockerExecutable: process.execPath,
dockerSocketPath: path.join(deploymentRoot, 'physical-docker.sock'),
allowRootService: uid === 0,
},
request: {
expectedGeneration: generation,
rolloutId,
startedAtMs: timestamp + generation * 10,
failureRollbackMutationId: crypto.randomUUID(),
failureRollbackChangedAtMs: timestamp + generation * 10 + 1,
},
};
const backupPath = path.join(
deploymentRoot,
'service',
'rollout-backups',
`${rolloutId}.sqlite`,
);
const evidence =
await products.rolloutSafety.createLocalSqliteRolloutBackup({
databasePath,
backupPath,
profile: 'edge',
});
const receiptPath = path.join(
deploymentRoot,
'service',
'rollouts',
`${rolloutId}.json`,
);
writePrivateNoReplace(
receiptPath,
`${JSON.stringify(
rolloutReceipt(
command,
evidence,
command.request.failureRollbackChangedAtMs,
),
null,
2,
)}\n`,
);
rolloutIds.push(rolloutId);
snapshots.push(evidence);
}
const collectionId = crypto.randomUUID();
const prepareCommand = {
schemaVersion: 1,
operation: 'local.deployment.compose.evidence-collection.prepare',
options: {
deploymentRoot,
allowRootService: uid === 0,
},
request: {
expectedGeneration: 4,
collectionId,
rolloutIds: [rolloutIds[0]],
restoreIds: [],
preparedAtMs: timestamp + 100,
},
};
const commitCommand = {
schemaVersion: 1,
operation: 'local.deployment.compose.evidence-collection.commit',
options: prepareCommand.options,
request: {
expectedGeneration: 4,
collectionId,
committedAtMs: timestamp + 101,
},
};
const prepared =
await products.deployment.collectLocalDeploymentComposeEvidence(
prepareCommand,
);
if (
prepared.status !== 'prepared' ||
prepared.collected.rolloutBackups !== 1
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'production collection prepare did not publish',
);
}
const targetPath = path.join(
deploymentRoot,
'service',
'rollout-backups',
`${rolloutIds[0]}.sqlite`,
);
const stagePath = path.join(
path.dirname(targetPath),
`.${path.basename(targetPath)}.ql3-collection-stage`,
);
fs.renameSync(targetPath, stagePath);
syncDirectory(path.dirname(targetPath));
const barrierIdentity = collectStorageIdentity(dataPath);
if (
barrierIdentity.bootId !== initialIdentity.bootId ||
barrierIdentity.blockDevice !== initialIdentity.blockDevice ||
barrierIdentity.sectorsWritten < initialIdentity.sectorsWritten
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'prepare block device identity changed',
);
}
const logicalSnapshotBytes = snapshots.reduce(
(total, snapshot) => total + snapshot.bytes,
0,
);
const deviceBytesWritten =
(barrierIdentity.sectorsWritten - initialIdentity.sectorsWritten) *
SECTOR_BYTES;
const writeAmplificationPermille = Math.ceil(
(deviceBytesWritten * 1000) / logicalSnapshotBytes,
);
const body = {
schemaVersion: 1,
evidenceClass: 'physical_edge_compose_storage_session',
sessionId,
manifestDigest: canonicalDigest(manifest),
uid,
preparedAt: new Date().toISOString(),
dataPath,
deploymentRoot,
barrier: {
identity: barrierIdentity,
sectorsWritten: barrierIdentity.sectorsWritten,
tree: collectTreeStorage(deploymentRoot),
},
prepareMeasurement: {
logicalSnapshotBytes,
deviceBytesWritten,
writeAmplificationPermille,
},
collection: {
snapshotCount: snapshots.length,
prepareCommand,
commitCommand,
target: {
rolloutId: rolloutIds[0],
snapshot: snapshotReceipt(snapshots[0]),
},
targetPath,
stagePath,
},
};
const session = Object.freeze({ ...body, sha256: canonicalDigest(body) });
writePrivateNoReplace(options.sessionPath, sessionContents(session));
process.stdout.write(
`${JSON.stringify({
schemaVersion: 1,
status: 'awaiting_external_reboot',
sessionId,
preparedGeneration: 4,
collectionStageDurable: true,
supported: false,
})}\n`,
);
}
function amplificationPermille(deviceBytes, logicalBytes) {
if (
!Number.isSafeInteger(deviceBytes) ||
deviceBytes < 0 ||
!Number.isSafeInteger(logicalBytes) ||
logicalBytes < 1
) {
return null;
}
return Math.ceil((deviceBytes * 1000) / logicalBytes);
}
function evaluateComposeStorageEvidence({
manifest,
dataPath,
targetSnapshot,
observed,
measurements,
outcomes,
}) {
const violations = [];
if (
!hasExactKeys(observed, REPORT_OBSERVED_KEYS) ||
!hasExactKeys(observed.before, STORAGE_IDENTITY_KEYS) ||
!hasExactKeys(observed.afterStart, STORAGE_IDENTITY_KEYS) ||
!hasExactKeys(observed.after, STORAGE_IDENTITY_KEYS)
) {
return Object.freeze(['observed storage identity shape is invalid']);
}
if (
observed.before.bootId === observed.after.bootId ||
observed.afterStart.bootId !== observed.after.bootId
) {
violations.push('boot identity did not prove a restart boundary');
}
for (const identity of [
observed.before,
observed.afterStart,
observed.after,
]) {
violations.push(...validateStorageIdentity(manifest, identity, dataPath));
if (
typeof identity.bootId !== 'string' ||
identity.bootId.length < 8 ||
!Number.isSafeInteger(identity.sectorsWritten) ||
identity.sectorsWritten < 0
) {
violations.push('storage identity counters are invalid');
}
}
if (
observed.before.blockDevice !== observed.afterStart.blockDevice ||
observed.afterStart.blockDevice !== observed.after.blockDevice ||
observed.after.sectorsWritten < observed.afterStart.sectorsWritten
) {
violations.push('block device identity or counter drifted');
}
if (
!hasExactKeys(measurements, REPORT_MEASUREMENT_KEYS) ||
REPORT_MEASUREMENT_KEYS.some(
(key) =>
!Number.isSafeInteger(measurements[key]) || measurements[key] < 0,
)
) {
violations.push('storage measurements are invalid');
}
if (
!hasExactKeys(outcomes, REPORT_OUTCOME_KEYS) ||
outcomes.commitStatus !== 'collected' ||
outcomes.replayStatus !== 'existing' ||
outcomes.sqliteIntegrity !== 'ok' ||
outcomes.stageRemoved !== true ||
outcomes.tombstonePresent !== true ||
outcomes.retainedSnapshots !== 2
) {
violations.push('production collection recovery outcome is invalid');
}
if (
Number.isSafeInteger(measurements.prepareWriteAmplificationPermille) &&
Number.isSafeInteger(measurements.resumeWriteAmplificationPermille) &&
(measurements.prepareWriteAmplificationPermille >
manifest.maximumPrepareWriteAmplificationPermille ||
measurements.resumeWriteAmplificationPermille >
manifest.maximumResumeWriteAmplificationPermille)
) {
violations.push('device write amplification exceeded manifest budget');
}
if (
!isSnapshot(targetSnapshot) ||
measurements.reclaimedAllocatedBytes < 1 ||
measurements.collectedSnapshotBytes !== targetSnapshot?.bytes ||
measurements.barrierAllocatedBytes - measurements.finalAllocatedBytes !==
measurements.reclaimedAllocatedBytes
) {
violations.push('snapshot allocation was not reclaimed');
}
if (
amplificationPermille(
measurements.prepareDeviceBytesWritten,
measurements.logicalSnapshotBytes,
) !== measurements.prepareWriteAmplificationPermille ||
amplificationPermille(
measurements.resumeDeviceBytesWritten,
measurements.collectedSnapshotBytes,
) !== measurements.resumeWriteAmplificationPermille
) {
violations.push('write amplification calculation drifted');
}
return Object.freeze(violations);
}
function buildComposeStorageReport({
manifest,
session,
observed,
measurements,
outcomes,
generatedAt,
}) {
const violations = evaluateComposeStorageEvidence({
manifest,
dataPath: session.dataPath,
targetSnapshot: session.collection.target.snapshot,
observed,
measurements,
outcomes,
});
const body = {
schemaVersion: 1,
evidenceClass: 'physical_edge_compose_storage_candidate',
supported: false,
generatedAt,
manifest,
session: {
sessionId: session.sessionId,
sessionDigest: session.sha256,
preparedAt: session.preparedAt,
targetRolloutId: session.collection.target.rolloutId,
targetSnapshot: session.collection.target.snapshot,
},
observed,
measurements,
outcomes,
qualification: {
passed: violations.length === 0,
violations,
measures: MEASURES,
doesNotProve: EXCLUSIONS,
},
};
return Object.freeze({ ...body, sha256: canonicalDigest(body) });
}
function validateComposeStorageReport(report, manifest, currentObserved) {
const violations = [];
if (!hasExactKeys(report, REPORT_KEYS)) {
return Object.freeze(['Compose storage report shape is invalid']);
}
const { sha256, ...body } = report;
if (!SHA256_PATTERN.test(sha256 ?? '') || canonicalDigest(body) !== sha256) {
violations.push('Compose storage report SHA-256 is invalid');
}
if (
report.schemaVersion !== 1 ||
report.evidenceClass !== 'physical_edge_compose_storage_candidate' ||
report.supported !== false ||
!Number.isFinite(Date.parse(report.generatedAt ?? ''))
) {
violations.push('Compose storage report identity is invalid');
}
if (
!hasExactKeys(report.session, REPORT_SESSION_KEYS) ||
!UUID_V4_PATTERN.test(report.session?.sessionId ?? '') ||
!SHA256_PATTERN.test(report.session?.sessionDigest ?? '') ||
!UUID_V4_PATTERN.test(report.session?.targetRolloutId ?? '') ||
!isSnapshot(report.session?.targetSnapshot) ||
!Number.isFinite(Date.parse(report.session?.preparedAt ?? '')) ||
!hasExactKeys(report.qualification, REPORT_QUALIFICATION_KEYS)
) {
violations.push('Compose storage report evidence shape is invalid');
}
if (
canonicalDigest(report.manifest) !== canonicalDigest(manifest) ||
report.manifest.deviceId !== manifest.deviceId
) {
violations.push('Compose storage report manifest did not match');
}
if (
report.observed?.after?.bootId !== currentObserved.bootId ||
report.observed?.after?.architecture !== currentObserved.architecture ||
report.observed?.after?.filesystem !== currentObserved.dataFilesystem ||
report.observed?.after?.mountPath !== currentObserved.dataPath
) {
violations.push('Compose storage report current device did not match');
}
const recomputed = evaluateComposeStorageEvidence({
manifest,
dataPath: currentObserved.dataPath,
targetSnapshot: report.session?.targetSnapshot,
observed: report.observed,
measurements: report.measurements,
outcomes: report.outcomes,
});
if (
report.qualification?.passed !== (recomputed.length === 0) ||
!Array.isArray(report.qualification?.violations) ||
JSON.stringify(report.qualification.violations) !==
JSON.stringify(recomputed) ||
JSON.stringify(report.qualification?.measures) !==
JSON.stringify(MEASURES) ||
JSON.stringify(report.qualification?.doesNotProve) !==
JSON.stringify(EXCLUSIONS) ||
recomputed.length > 0
) {
violations.push('Compose storage report qualification was widened');
}
return Object.freeze(violations);
}
async function resumePhase(options, manifest) {
const session = normalizeSession(
readBoundedJson(options.sessionPath, 'session'),
);
if (session.manifestDigest !== canonicalDigest(manifest)) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'session manifest digest did not match',
);
}
if (session.uid !== process.getuid?.()) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'session POSIX identity changed',
);
}
const dataPath = fs.realpathSync(session.dataPath);
if (dataPath !== session.dataPath) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'session dataPath is no longer canonical',
);
}
assertPrivateDirectory(dataPath, 'dataPath');
assertChildPath(session.dataPath, options.sessionPath, 'sessionPath');
assertChildPath(session.dataPath, options.outputPath, 'outputPath');
const deploymentRoot = fs.realpathSync(session.deploymentRoot);
if (deploymentRoot !== session.deploymentRoot) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'session deploymentRoot is no longer canonical',
);
}
assertPrivateDirectory(deploymentRoot, 'deploymentRoot');
const preparedIdentityViolations = validateStorageIdentity(
manifest,
session.barrier.identity,
session.dataPath,
);
if (preparedIdentityViolations.length > 0) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`prepared storage identity rejected: ${preparedIdentityViolations.join(
'; ',
)}`,
);
}
const products = requireBuiltProducts();
const afterStart = collectStorageIdentity(session.dataPath);
const resumeIdentityViolations = validateStorageIdentity(
manifest,
afterStart,
session.dataPath,
);
if (resumeIdentityViolations.length > 0) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
`resume storage identity rejected: ${resumeIdentityViolations.join(
'; ',
)}`,
);
}
if (afterStart.bootId === session.barrier.identity.bootId) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'resume requires a different Linux boot identity',
);
}
if (
afterStart.blockDevice !== session.barrier.identity.blockDevice ||
afterStart.filesystem !== session.barrier.identity.filesystem
) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'storage identity changed across reboot',
);
}
const committed =
await products.deployment.collectLocalDeploymentComposeEvidence(
session.collection.commitCommand,
);
const replay =
await products.deployment.collectLocalDeploymentComposeEvidence(
session.collection.commitCommand,
);
const databaseEvidence =
await products.rolloutSafety.inspectLocalSqliteSnapshot({
databasePath: path.join(session.deploymentRoot, 'qinglong3.sqlite'),
profile: 'edge',
});
const after = collectStorageIdentity(session.dataPath);
const afterTree = collectTreeStorage(session.deploymentRoot);
const retainedSnapshots = fs
.readdirSync(
path.join(session.deploymentRoot, 'service', 'rollout-backups'),
)
.filter((entry) => entry.endsWith('.sqlite')).length;
const tombstonePath = path.join(
session.deploymentRoot,
'service',
'collected-evidence',
'rollout-backups',
`${session.collection.target.rolloutId}.json`,
);
const resumeDeviceBytesWritten =
(after.sectorsWritten - afterStart.sectorsWritten) * SECTOR_BYTES;
const reclaimedAllocatedBytes =
session.barrier.tree.allocatedBytes - afterTree.allocatedBytes;
const measurements = Object.freeze({
logicalSnapshotBytes: session.prepareMeasurement.logicalSnapshotBytes,
collectedSnapshotBytes: session.collection.target.snapshot.bytes,
prepareDeviceBytesWritten: session.prepareMeasurement.deviceBytesWritten,
resumeDeviceBytesWritten,
prepareWriteAmplificationPermille:
session.prepareMeasurement.writeAmplificationPermille,
resumeWriteAmplificationPermille: amplificationPermille(
resumeDeviceBytesWritten,
session.collection.target.snapshot.bytes,
),
barrierAllocatedBytes: session.barrier.tree.allocatedBytes,
finalAllocatedBytes: afterTree.allocatedBytes,
reclaimedAllocatedBytes,
});
const outcomes = Object.freeze({
commitStatus: committed.status,
replayStatus: replay.status,
sqliteIntegrity: databaseEvidence.contractVersion === 44 ? 'ok' : 'invalid',
stageRemoved: !fs.existsSync(session.collection.stagePath),
tombstonePresent: fs.existsSync(tombstonePath),
retainedSnapshots,
});
const report = buildComposeStorageReport({
manifest,
session,
observed: {
before: session.barrier.identity,
afterStart,
after,
},
measurements,
outcomes,
generatedAt: new Date().toISOString(),
});
writePrivateNoReplace(
options.outputPath,
`${JSON.stringify(report, null, options.json ? 0 : 2)}\n`,
);
process.stdout.write(
`${JSON.stringify(report, null, options.json ? 0 : 2)}\n`,
);
if (!report.qualification.passed) process.exitCode = 1;
}
async function main() {
if (Number(process.versions.node.split('.')[0]) < 24) {
throw new QingLong3PhysicalComposeStorageEvidenceError(
'Node.js 24 or newer is required',
);
}
const options = parseArguments(process.argv.slice(2));
const manifest = readManifest(options.manifestPath);
if (options.phase === 'prepare') {
await preparePhase(options, manifest);
} else {
await resumePhase(options, manifest);
}
}
module.exports = {
QingLong3PhysicalComposeStorageEvidenceError,
amplificationPermille,
buildComposeStorageReport,
evaluateComposeStorageEvidence,
normalizeComposeStorageManifest,
normalizeSession,
parseArguments,
parseSectorsWritten,
validateComposeStorageReport,
validateStorageIdentity,
};
if (require.main === module) {
main().catch((error) => {
process.stderr.write(
`${error instanceof Error ? error.message : String(error)}\n`,
);
process.exitCode = 1;
});
}