feat(ql3): add copilot diagnosis output keyring

This commit is contained in:
whyour
2026-08-15 21:04:21 +08:00
parent 453a7679b2
commit c3bd6d40bb
8 changed files with 635 additions and 2 deletions
@@ -35,6 +35,11 @@
"require": "./dist/application-runtime/aiProductionApplication.js",
"default": "./dist/application-runtime/aiProductionApplication.js"
},
"./failure-diagnosis-output-keyring": {
"types": "./dist/copilot/failure-diagnosis/outputProjectedKeyring.d.ts",
"require": "./dist/copilot/failure-diagnosis/outputProjectedKeyring.js",
"default": "./dist/copilot/failure-diagnosis/outputProjectedKeyring.js"
},
"./http": {
"types": "./dist/transport/httpSurface.d.ts",
"require": "./dist/transport/httpSurface.js",
@@ -0,0 +1,196 @@
import { Buffer } from 'node:buffer';
import { createHash } from 'node:crypto';
export const CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_SCHEMA =
'qinglong/copilot-failure-diagnosis-output-projected-keyring@v1' as const;
export const MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_BYTES =
64 * 1024;
export const MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_PROJECTED_KEYS = 16;
const KEY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
const BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
const PROJECTION_DIGEST_DOMAIN = Buffer.from(
'qinglong/copilot-failure-diagnosis-output-projected-keyring-digest@v1\0',
'utf8',
);
export interface ClusterCopilotFailureDiagnosisOutputKeyringManifest {
readonly schema: typeof CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_SCHEMA;
readonly activeKeyId: string;
readonly keys: Readonly<Record<string, string>>;
}
export interface ClusterCopilotFailureDiagnosisOutputKeyringSummary {
readonly schemaVersion: 1;
readonly activeKeyId: string;
readonly keyIds: readonly string[];
readonly projectionDigest: string;
}
export interface ClusterCopilotFailureDiagnosisOutputKeyMaterial {
readonly keyId: string;
readonly key: Uint8Array;
}
export class InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError extends TypeError {
readonly code =
'QL3_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_INVALID';
constructor() {
super('Cluster Copilot failure diagnosis output keyring manifest is invalid');
this.name =
'InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError';
}
}
function invalid(): never {
throw new InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError();
}
function dataRecord(value: unknown): Record<string, unknown> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
Object.getPrototypeOf(value) !== Object.prototype
) {
return invalid();
}
return value as Record<string, unknown>;
}
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])
);
}
export function normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest(
value: unknown,
): Readonly<ClusterCopilotFailureDiagnosisOutputKeyringManifest> {
const manifest = dataRecord(value);
if (
!exactKeys(manifest, ['activeKeyId', 'keys', 'schema']) ||
manifest.schema !==
CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_SCHEMA ||
typeof manifest.activeKeyId !== 'string' ||
!KEY_ID_PATTERN.test(manifest.activeKeyId)
) {
return invalid();
}
const keys = dataRecord(manifest.keys);
const entries = Object.entries(keys).sort(([left], [right]) =>
left < right ? -1 : left > right ? 1 : 0,
);
if (
entries.length < 1 ||
entries.length >
MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_PROJECTED_KEYS
) {
return invalid();
}
const normalized: (readonly [string, string])[] = [];
for (const [keyId, encoded] of entries) {
let material: Buffer | undefined;
try {
if (
!KEY_ID_PATTERN.test(keyId) ||
typeof encoded !== 'string' ||
!BASE64URL_PATTERN.test(encoded)
) {
return invalid();
}
material = Buffer.from(encoded, 'base64url');
if (
material.byteLength !== 32 ||
material.toString('base64url') !== encoded
) {
return invalid();
}
normalized.push(Object.freeze([keyId, encoded] as const));
} finally {
material?.fill(0);
}
}
const normalizedKeys = Object.freeze(Object.fromEntries(normalized));
if (normalizedKeys[manifest.activeKeyId] === undefined) return invalid();
return Object.freeze({
schema:
CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_SCHEMA,
activeKeyId: manifest.activeKeyId,
keys: normalizedKeys,
});
}
export function parseClusterCopilotFailureDiagnosisOutputKeyringManifest(
bytes: Buffer,
): Readonly<ClusterCopilotFailureDiagnosisOutputKeyringManifest> {
try {
if (
!Buffer.isBuffer(bytes) ||
bytes.byteLength < 1 ||
bytes.byteLength >
MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_BYTES
) {
return invalid();
}
return normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest(
JSON.parse(bytes.toString('utf8')),
);
} catch (error) {
if (
error instanceof
InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError
) {
throw error;
}
return invalid();
}
}
export function canonicalClusterCopilotFailureDiagnosisOutputKeyringManifest(
value: ClusterCopilotFailureDiagnosisOutputKeyringManifest,
): Buffer {
const manifest =
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest(value);
return Buffer.from(`${JSON.stringify(manifest)}\n`, 'utf8');
}
export function resolveClusterCopilotFailureDiagnosisOutputKeyringMaterial(
value: ClusterCopilotFailureDiagnosisOutputKeyringManifest,
keyId: string,
): Readonly<ClusterCopilotFailureDiagnosisOutputKeyMaterial> | null {
if (typeof keyId !== 'string' || !KEY_ID_PATTERN.test(keyId)) {
return invalid();
}
const manifest =
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest(value);
const encoded = manifest.keys[keyId];
if (encoded === undefined) return null;
const key = Buffer.from(encoded, 'base64url');
if (key.byteLength !== 32 || key.toString('base64url') !== encoded) {
key.fill(0);
return invalid();
}
return Object.freeze({ keyId, key });
}
export function summarizeClusterCopilotFailureDiagnosisOutputKeyringManifest(
value: ClusterCopilotFailureDiagnosisOutputKeyringManifest,
): Readonly<ClusterCopilotFailureDiagnosisOutputKeyringSummary> {
const manifest =
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest(value);
const keyIds = Object.freeze(Object.keys(manifest.keys).sort());
return Object.freeze({
schemaVersion: 1 as const,
activeKeyId: manifest.activeKeyId,
keyIds,
projectionDigest: createHash('sha256')
.update(PROJECTION_DIGEST_DOMAIN)
.update(JSON.stringify(manifest))
.digest('hex'),
});
}
@@ -0,0 +1,176 @@
import { Buffer } from 'node:buffer';
import { PrivateProjectedFileReader } from '../../security/privateProjectedFile';
import {
MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_BYTES,
canonicalClusterCopilotFailureDiagnosisOutputKeyringManifest,
parseClusterCopilotFailureDiagnosisOutputKeyringManifest,
resolveClusterCopilotFailureDiagnosisOutputKeyringMaterial,
summarizeClusterCopilotFailureDiagnosisOutputKeyringManifest,
type ClusterCopilotFailureDiagnosisOutputKeyMaterial,
type ClusterCopilotFailureDiagnosisOutputKeyringManifest,
type ClusterCopilotFailureDiagnosisOutputKeyringSummary,
} from './outputKeyringManifest';
const DATA_FILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,252}$/;
export {
CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_SCHEMA,
MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_BYTES,
MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_PROJECTED_KEYS,
InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError,
canonicalClusterCopilotFailureDiagnosisOutputKeyringManifest,
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest,
parseClusterCopilotFailureDiagnosisOutputKeyringManifest,
resolveClusterCopilotFailureDiagnosisOutputKeyringMaterial,
summarizeClusterCopilotFailureDiagnosisOutputKeyringManifest,
type ClusterCopilotFailureDiagnosisOutputKeyringManifest,
type ClusterCopilotFailureDiagnosisOutputKeyringSummary,
} from './outputKeyringManifest';
export interface ClusterCopilotFailureDiagnosisOutputProjectedKeyringOptions {
readonly rootDirectory: string;
readonly dataFileName?: string;
}
export interface ClusterCopilotFailureDiagnosisOutputKeyProvider {
active(): Promise<ClusterCopilotFailureDiagnosisOutputKeyMaterial>;
resolve(
keyId: string,
): Promise<ClusterCopilotFailureDiagnosisOutputKeyMaterial | null>;
}
export class ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError extends Error {
readonly code =
'QL3_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_PROJECTED_KEYRING_UNAVAILABLE';
constructor(options?: ErrorOptions) {
super(
'Projected Cluster Copilot failure diagnosis output keyring is unavailable',
options,
);
this.name =
'ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError';
}
}
function unavailable(
cause?: unknown,
): ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError {
return new ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError(
{ cause: cause instanceof Error ? cause : undefined },
);
}
function dataFileName(value: unknown): string {
if (typeof value !== 'string' || !DATA_FILE_NAME.test(value)) {
throw unavailable();
}
return value;
}
async function readManifest(
reader: PrivateProjectedFileReader,
fileName: string,
): Promise<Readonly<ClusterCopilotFailureDiagnosisOutputKeyringManifest>> {
let bytes: Buffer | undefined;
let canonical: Buffer | undefined;
try {
bytes = await reader.read(fileName);
const manifest =
parseClusterCopilotFailureDiagnosisOutputKeyringManifest(bytes);
canonical =
canonicalClusterCopilotFailureDiagnosisOutputKeyringManifest(manifest);
if (!canonical.equals(bytes)) throw unavailable();
return manifest;
} catch (cause) {
throw cause instanceof
ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError
? cause
: unavailable(cause);
} finally {
bytes?.fill(0);
canonical?.fill(0);
}
}
/** Read-only, no-cache Copilot diagnosis output key authority. */
export class ClusterCopilotFailureDiagnosisOutputProjectedKeyring
implements ClusterCopilotFailureDiagnosisOutputKeyProvider
{
readonly #reader: PrivateProjectedFileReader;
readonly #dataFileName: string;
constructor(
options: ClusterCopilotFailureDiagnosisOutputProjectedKeyringOptions,
) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw unavailable();
}
try {
this.#reader = new PrivateProjectedFileReader({
rootDirectory: options.rootDirectory,
minimumBytes: 1,
maximumBytes:
MAX_CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_BYTES,
access: 'read_only_keyring',
});
this.#dataFileName = dataFileName(options.dataFileName ?? 'keyring.json');
} catch (cause) {
throw unavailable(cause);
}
}
async verify(): Promise<
Readonly<ClusterCopilotFailureDiagnosisOutputKeyringSummary>
> {
return summarizeClusterCopilotFailureDiagnosisOutputKeyringManifest(
await readManifest(this.#reader, this.#dataFileName),
);
}
async active(): Promise<ClusterCopilotFailureDiagnosisOutputKeyMaterial> {
try {
const manifest = await readManifest(this.#reader, this.#dataFileName);
const material =
resolveClusterCopilotFailureDiagnosisOutputKeyringMaterial(
manifest,
manifest.activeKeyId,
);
if (!material) throw unavailable();
return material;
} catch (cause) {
throw cause instanceof
ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError
? cause
: unavailable(cause);
}
}
async resolve(
keyId: string,
): Promise<ClusterCopilotFailureDiagnosisOutputKeyMaterial | null> {
try {
return resolveClusterCopilotFailureDiagnosisOutputKeyringMaterial(
await readManifest(this.#reader, this.#dataFileName),
keyId,
);
} catch (cause) {
throw cause instanceof
ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError
? cause
: unavailable(cause);
}
}
}
export async function createClusterCopilotFailureDiagnosisOutputProjectedKeyring(
options: ClusterCopilotFailureDiagnosisOutputProjectedKeyringOptions,
): Promise<
Readonly<ClusterCopilotFailureDiagnosisOutputProjectedKeyring>
> {
const provider =
new ClusterCopilotFailureDiagnosisOutputProjectedKeyring(options);
await provider.verify();
return provider;
}
@@ -0,0 +1,178 @@
const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const { afterEach, test } = require('node:test');
const rootExport = require('@qinglong/cluster-control');
const {
CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_SCHEMA,
ClusterCopilotFailureDiagnosisOutputProjectedKeyring,
ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError,
InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError,
canonicalClusterCopilotFailureDiagnosisOutputKeyringManifest,
createClusterCopilotFailureDiagnosisOutputProjectedKeyring,
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest,
} = require('@qinglong/cluster-control/failure-diagnosis-output-keyring');
const roots = [];
afterEach(async () => {
await Promise.all(
roots
.splice(0)
.map((root) => fs.rm(root, { recursive: true, force: true })),
);
});
async function tempRoot(label = 'ql3-copilot-output-keyring-') {
const root = await fs.mkdtemp(path.join(os.tmpdir(), label));
roots.push(root);
return root;
}
function manifest(activeKeyId, keys) {
return Object.freeze({
schema:
CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_KEYRING_MANIFEST_SCHEMA,
activeKeyId,
keys: Object.freeze(keys),
});
}
async function publish(root, generationName, value, mode = 0o440) {
const generation = path.join(root, generationName);
await fs.mkdir(generation, { mode: 0o750 });
const target = path.join(generation, 'keyring.json');
await fs.writeFile(
target,
canonicalClusterCopilotFailureDiagnosisOutputKeyringManifest(value),
{ mode },
);
await fs.chmod(target, mode);
const next = path.join(root, '..data-next');
await fs.symlink(generationName, next);
await fs.rename(next, path.join(root, '..data'));
try {
await fs.symlink('..data/keyring.json', path.join(root, 'keyring.json'));
} catch (error) {
if (error.code !== 'EEXIST') throw error;
}
}
test('projected Copilot output keyring rotates active material without caching', async () => {
const root = await tempRoot();
const keyOne = Buffer.alloc(32, 0x31);
const keyTwo = Buffer.alloc(32, 0x32);
await publish(
root,
'..2026_08_15_01',
manifest('copilot-output-one', {
'copilot-output-one': keyOne.toString('base64url'),
}),
);
const provider =
await createClusterCopilotFailureDiagnosisOutputProjectedKeyring({
rootDirectory: root,
});
const first = await provider.active();
assert.equal(first.keyId, 'copilot-output-one');
assert.deepEqual(Buffer.from(first.key), keyOne);
first.key.fill(0);
await publish(
root,
'..2026_08_15_02',
manifest('copilot-output-two', {
'copilot-output-one': keyOne.toString('base64url'),
'copilot-output-two': keyTwo.toString('base64url'),
}),
);
const second = await provider.active();
assert.equal(second.keyId, 'copilot-output-two');
assert.deepEqual(Buffer.from(second.key), keyTwo);
second.key.fill(0);
const historical = await provider.resolve('copilot-output-one');
assert.ok(historical);
assert.deepEqual(Buffer.from(historical.key), keyOne);
historical.key.fill(0);
assert.equal(await provider.resolve('missing-key'), null);
const summary = await provider.verify();
assert.deepEqual(summary.keyIds, [
'copilot-output-one',
'copilot-output-two',
]);
assert.equal(summary.activeKeyId, 'copilot-output-two');
assert.match(summary.projectionDigest, /^[0-9a-f]{64}$/);
});
test('manifest rejects missing active, wrong domains and non-canonical material', () => {
const key = Buffer.alloc(32, 0x41).toString('base64url');
assert.throws(
() =>
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest({
...manifest('missing-key', { 'copilot-output-one': key }),
}),
InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError,
);
assert.throws(
() =>
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest({
...manifest('copilot-output-one', { 'copilot-output-one': key }),
schema: 'qinglong/cluster-tool-invocation-projected-keyring@v1',
}),
InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError,
);
assert.throws(
() =>
normalizeClusterCopilotFailureDiagnosisOutputKeyringManifest(
manifest('copilot-output-one', {
'copilot-output-one': Buffer.alloc(31, 0x41).toString('base64url'),
}),
),
InvalidClusterCopilotFailureDiagnosisOutputKeyringManifestError,
);
assert.equal(
rootExport.ClusterCopilotFailureDiagnosisOutputProjectedKeyring,
undefined,
);
});
test('projected Copilot output keyring rejects writable and escaping files', async () => {
const writableRoot = await tempRoot();
const key = Buffer.alloc(32, 0x51).toString('base64url');
await publish(
writableRoot,
'..2026_08_15_01',
manifest('copilot-output-one', { 'copilot-output-one': key }),
0o640,
);
await assert.rejects(
() =>
new ClusterCopilotFailureDiagnosisOutputProjectedKeyring({
rootDirectory: writableRoot,
}).verify(),
ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError,
);
const escapeRoot = await tempRoot();
const outsideRoot = await tempRoot('ql3-copilot-output-outside-');
const outsideFile = path.join(outsideRoot, 'keyring.json');
await fs.writeFile(
outsideFile,
canonicalClusterCopilotFailureDiagnosisOutputKeyringManifest(
manifest('copilot-output-one', { 'copilot-output-one': key }),
),
{ mode: 0o440 },
);
await fs.chmod(outsideFile, 0o440);
await fs.symlink(outsideFile, path.join(escapeRoot, 'keyring.json'));
await assert.rejects(
() =>
new ClusterCopilotFailureDiagnosisOutputProjectedKeyring({
rootDirectory: escapeRoot,
}).verify(),
ClusterCopilotFailureDiagnosisOutputProjectedKeyringUnavailableError,
);
});