feat(ql3): add cluster tool result key authority

This commit is contained in:
whyour
2026-08-15 00:40:56 +08:00
parent 93690dd921
commit aae96390cc
10 changed files with 829 additions and 91 deletions
@@ -120,6 +120,11 @@
"require": "./dist/remote-execution/mountedSecretProvider.js",
"default": "./dist/remote-execution/mountedSecretProvider.js"
},
"./trusted-tool-result-keyring": {
"types": "./dist/trusted-tool/key-management/toolResultProjectedKeyring.d.ts",
"require": "./dist/trusted-tool/key-management/toolResultProjectedKeyring.js",
"default": "./dist/trusted-tool/key-management/toolResultProjectedKeyring.js"
},
"./remote-completion": {
"types": "./dist/remote-execution/remoteWorkerCompletionService.d.ts",
"require": "./dist/remote-execution/remoteWorkerCompletionService.js",
@@ -1,7 +1,4 @@
// Remote Execution owns mounted Secret resolution for authenticated delivery.
import { constants } from 'node:fs';
import { lstat, open, realpath } from 'node:fs/promises';
import { isAbsolute, join, normalize, parse, relative } from 'node:path';
import {
MAX_REMOTE_SECRET_DELIVERY_TOTAL_VALUE_BYTES,
MAX_REMOTE_SECRET_VALUE_BYTES,
@@ -12,7 +9,7 @@ import {
} from '@qinglong/runtime-core/remote-secret-delivery';
import { secretProjectionFileName } from '@qinglong/runtime-core/secret-projection';
const MAX_SECRET_ROOT_BYTES = 4096;
import { PrivateProjectedFileReader } from '../security/privateProjectedFile';
export interface ClusterMountedSecretProviderOptions {
/**
@@ -38,20 +35,6 @@ export class ClusterMountedSecretProviderError extends Error {
}
}
function rootDirectory(value: string): string {
if (
typeof value !== 'string' ||
!isAbsolute(value) ||
parse(value).root === value ||
normalize(value) !== value ||
value.includes('\0') ||
Buffer.byteLength(value, 'utf8') > MAX_SECRET_ROOT_BYTES
) {
throw new ClusterMountedSecretProviderError('invalid_configuration');
}
return value;
}
/**
* Kubernetes Secret keys cannot contain a SecretRef directly. This stable,
* non-reversible name also prevents Project/name input from becoming a path.
@@ -66,72 +49,6 @@ export function clusterMountedSecretFileName(secretRef: string): string {
}
}
function remainsBelow(root: string, candidate: string): boolean {
const suffix = relative(root, candidate);
return (
suffix.length > 0 &&
!isAbsolute(suffix) &&
suffix !== '..' &&
!suffix.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`)
);
}
async function resolvedRoot(path: string): Promise<string> {
try {
const configured = await lstat(path);
if (!configured.isDirectory() || configured.isSymbolicLink()) {
throw new Error('root is not a direct directory');
}
return await realpath(path);
} catch (error) {
throw new ClusterMountedSecretProviderError('root_unavailable', {
cause: error,
});
}
}
async function readMaterial(root: string, secretRef: string): Promise<Buffer> {
const candidate = join(root, clusterMountedSecretFileName(secretRef));
let handle;
try {
const target = await realpath(candidate);
if (!remainsBelow(root, target)) {
throw new Error('material escaped its root');
}
handle = await open(
target,
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
);
const stat = await handle.stat();
if (
!stat.isFile() ||
stat.nlink !== 1 ||
stat.size < 0 ||
stat.size > MAX_REMOTE_SECRET_VALUE_BYTES ||
(stat.mode & 0o111) !== 0 ||
(stat.mode & 0o027) !== 0
) {
throw new Error('material metadata is unsafe');
}
const bytes = await handle.readFile();
if (
bytes.byteLength !== stat.size ||
bytes.byteLength > MAX_REMOTE_SECRET_VALUE_BYTES ||
(await realpath(candidate)) !== target
) {
bytes.fill(0);
throw new Error('material changed while reading');
}
return bytes;
} catch (error) {
throw new ClusterMountedSecretProviderError('material_unavailable', {
cause: error,
});
} finally {
await handle?.close().catch(() => undefined);
}
}
function secretValue(bytes: Buffer): string {
try {
const value = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
@@ -155,17 +72,34 @@ function secretValue(bytes: Buffer): string {
export class ClusterMountedSecretProvider
implements RemoteWorkerSecretValueProvider
{
private readonly rootDirectory: string;
private readonly reader: PrivateProjectedFileReader;
constructor(options: ClusterMountedSecretProviderOptions) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new ClusterMountedSecretProviderError('invalid_configuration');
}
this.rootDirectory = rootDirectory(options.rootDirectory);
try {
this.reader = new PrivateProjectedFileReader({
rootDirectory: options.rootDirectory,
minimumBytes: 0,
maximumBytes: MAX_REMOTE_SECRET_VALUE_BYTES,
access: 'private_material',
});
} catch (error) {
throw new ClusterMountedSecretProviderError('invalid_configuration', {
cause: error,
});
}
}
async verify(): Promise<void> {
await resolvedRoot(this.rootDirectory);
try {
await this.reader.verify();
} catch (error) {
throw new ClusterMountedSecretProviderError('root_unavailable', {
cause: error,
});
}
}
async resolve(
@@ -179,13 +113,19 @@ export class ClusterMountedSecretProvider
cause: error,
});
}
const root = await resolvedRoot(this.rootDirectory);
const buffers: Buffer[] = [];
try {
const values = [];
let totalBytes = 0;
for (const secretRef of normalized.secretRefs) {
const bytes = await readMaterial(root, secretRef);
const bytes = await this.reader
.read(clusterMountedSecretFileName(secretRef))
.catch((error) => {
throw new ClusterMountedSecretProviderError(
'material_unavailable',
{ cause: error },
);
});
buffers.push(bytes);
totalBytes += bytes.byteLength;
if (totalBytes > MAX_REMOTE_SECRET_DELIVERY_TOTAL_VALUE_BYTES) {
@@ -0,0 +1,168 @@
import { Buffer } from 'node:buffer';
import { constants } from 'node:fs';
import { lstat, open, realpath } from 'node:fs/promises';
import { isAbsolute, join, normalize, parse, relative } from 'node:path';
const MAX_ROOT_DIRECTORY_BYTES = 4096;
const FILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,252}$/;
export interface PrivateProjectedFileReaderOptions {
readonly rootDirectory: string;
readonly minimumBytes: number;
readonly maximumBytes: number;
readonly access: 'private_material' | 'read_only_keyring';
}
export class PrivateProjectedFileError extends Error {
constructor(options?: ErrorOptions) {
super('Private projected file is unavailable', options);
this.name = 'PrivateProjectedFileError';
}
}
function unavailable(cause?: unknown): never {
throw new PrivateProjectedFileError({
cause: cause instanceof Error ? cause : undefined,
});
}
function rootDirectory(value: unknown): string {
if (
typeof value !== 'string' ||
!isAbsolute(value) ||
parse(value).root === value ||
normalize(value) !== value ||
value.includes('\0') ||
Buffer.byteLength(value, 'utf8') > MAX_ROOT_DIRECTORY_BYTES
) {
return unavailable();
}
return value;
}
function boundedInteger(value: unknown, minimum: number): number {
if (
typeof value !== 'number' ||
!Number.isSafeInteger(value) ||
value < minimum
) {
return unavailable();
}
return value;
}
function fileName(value: unknown): string {
if (typeof value !== 'string' || !FILE_NAME_PATTERN.test(value)) {
return unavailable();
}
return value;
}
function remainsBelow(root: string, candidate: string): boolean {
const suffix = relative(root, candidate);
return (
suffix.length > 0 &&
!isAbsolute(suffix) &&
suffix !== '..' &&
!suffix.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`)
);
}
function safeMode(
mode: number,
access: PrivateProjectedFileReaderOptions['access'],
): boolean {
if ((mode & 0o111) !== 0 || (mode & 0o007) !== 0) return false;
return access === 'private_material'
? (mode & 0o027) === 0
: (mode & 0o222) === 0 && (mode & 0o440) !== 0;
}
/** Internal, no-cache reader for Kubernetes atomic-writer style projections. */
export class PrivateProjectedFileReader {
readonly #rootDirectory: string;
readonly #minimumBytes: number;
readonly #maximumBytes: number;
readonly #access: PrivateProjectedFileReaderOptions['access'];
constructor(options: PrivateProjectedFileReaderOptions) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
unavailable();
}
const normalizedRoot = rootDirectory(options.rootDirectory);
const minimumBytes = boundedInteger(options.minimumBytes, 0);
const maximumBytes = boundedInteger(options.maximumBytes, 1);
if (
maximumBytes < minimumBytes ||
(options.access !== 'private_material' &&
options.access !== 'read_only_keyring')
) {
unavailable();
}
this.#rootDirectory = normalizedRoot;
this.#minimumBytes = minimumBytes;
this.#maximumBytes = maximumBytes;
this.#access = options.access;
}
async #resolvedRoot(): Promise<string> {
try {
const stat = await lstat(this.#rootDirectory);
if (!stat.isDirectory() || stat.isSymbolicLink()) return unavailable();
return await realpath(this.#rootDirectory);
} catch (cause) {
return unavailable(cause);
}
}
async verify(): Promise<void> {
await this.#resolvedRoot();
}
async read(name: string): Promise<Buffer> {
const normalizedName = fileName(name);
let handle: Awaited<ReturnType<typeof open>> | undefined;
let bytes: Buffer | undefined;
try {
const root = await this.#resolvedRoot();
const candidate = join(root, normalizedName);
const target = await realpath(candidate);
if (!remainsBelow(root, target)) return unavailable();
handle = await open(
target,
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
);
const before = await handle.stat();
if (
!before.isFile() ||
before.nlink !== 1 ||
before.size < this.#minimumBytes ||
before.size > this.#maximumBytes ||
!safeMode(before.mode, this.#access)
) {
return unavailable();
}
bytes = await handle.readFile();
const after = await handle.stat();
if (
bytes.byteLength !== before.size ||
after.dev !== before.dev ||
after.ino !== before.ino ||
after.size !== before.size ||
after.mtimeMs !== before.mtimeMs ||
(await realpath(candidate)) !== target ||
(await realpath(this.#rootDirectory)) !== root
) {
return unavailable();
}
const owned = bytes;
bytes = undefined;
return owned;
} catch (cause) {
return unavailable(cause);
} finally {
bytes?.fill(0);
await handle?.close().catch(() => undefined);
}
}
}
@@ -0,0 +1,194 @@
import { Buffer } from 'node:buffer';
import { createHash } from 'node:crypto';
import { toolResultKeyMaterialProof } from '@qinglong/runtime-core/tool-result-key-catalog';
export const CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA =
'qinglong/cluster-tool-result-projected-keyring@v1' as const;
export const MAX_CLUSTER_TOOL_RESULT_KEYRING_BYTES = 64 * 1024;
export const MAX_CLUSTER_TOOL_RESULT_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/cluster-tool-result-projected-keyring-digest@v1\0',
'utf8',
);
export interface ClusterToolResultKeyringManifest {
readonly schema: typeof CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA;
readonly keys: Readonly<Record<string, string>>;
}
export interface ClusterToolResultKeyringSummary {
readonly schemaVersion: 1;
readonly keyIds: readonly string[];
readonly materialProofs: Readonly<Record<string, string>>;
readonly projectionDigest: string;
}
export class InvalidClusterToolResultKeyringManifestError extends TypeError {
readonly code = 'QL3_CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_INVALID';
constructor() {
super('Cluster Tool result keyring manifest is invalid');
this.name = 'InvalidClusterToolResultKeyringManifestError';
}
}
function invalid(): never {
throw new InvalidClusterToolResultKeyringManifestError();
}
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 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 normalizedEntries(
value: unknown,
): readonly (readonly [string, string])[] {
const keys = dataRecord(value);
const entries = Object.entries(keys).sort(([left], [right]) =>
left < right ? -1 : left > right ? 1 : 0,
);
if (
entries.length < 1 ||
entries.length > MAX_CLUSTER_TOOL_RESULT_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);
}
}
return Object.freeze(normalized);
}
export function normalizeClusterToolResultKeyringManifest(
value: unknown,
): Readonly<ClusterToolResultKeyringManifest> {
const manifest = dataRecord(value);
if (
!exactKeys(manifest, ['keys', 'schema']) ||
manifest.schema !== CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA
) {
return invalid();
}
return Object.freeze({
schema: CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA,
keys: Object.freeze(Object.fromEntries(normalizedEntries(manifest.keys))),
});
}
export function parseClusterToolResultKeyringManifest(
bytes: Buffer,
): Readonly<ClusterToolResultKeyringManifest> {
try {
if (
!Buffer.isBuffer(bytes) ||
bytes.byteLength < 1 ||
bytes.byteLength > MAX_CLUSTER_TOOL_RESULT_KEYRING_BYTES
) {
return invalid();
}
return normalizeClusterToolResultKeyringManifest(
JSON.parse(bytes.toString('utf8')),
);
} catch (error) {
if (error instanceof InvalidClusterToolResultKeyringManifestError) {
throw error;
}
return invalid();
}
}
export function canonicalClusterToolResultKeyringManifest(
value: ClusterToolResultKeyringManifest,
): Buffer {
const manifest = normalizeClusterToolResultKeyringManifest(value);
return Buffer.from(`${JSON.stringify(manifest)}\n`, 'utf8');
}
export function resolveClusterToolResultKeyringMaterial(
value: ClusterToolResultKeyringManifest,
keyId: string,
): Readonly<{ keyId: string; key: Uint8Array }> | null {
if (typeof keyId !== 'string' || !KEY_ID_PATTERN.test(keyId))
return invalid();
const manifest = normalizeClusterToolResultKeyringManifest(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 summarizeClusterToolResultKeyringManifest(
value: ClusterToolResultKeyringManifest,
): Readonly<ClusterToolResultKeyringSummary> {
const manifest = normalizeClusterToolResultKeyringManifest(value);
const keyIds = Object.keys(manifest.keys).sort();
const materialProofs: Record<string, string> = Object.create(null);
for (const keyId of keyIds) {
const material = resolveClusterToolResultKeyringMaterial(manifest, keyId)!;
try {
materialProofs[keyId] = toolResultKeyMaterialProof(keyId, material.key);
} finally {
material.key.fill(0);
}
}
const canonicalProofs = Object.freeze({ ...materialProofs });
return Object.freeze({
schemaVersion: 1 as const,
keyIds: Object.freeze(keyIds),
materialProofs: canonicalProofs,
projectionDigest: createHash('sha256')
.update(PROJECTION_DIGEST_DOMAIN)
.update(
JSON.stringify({
schema: manifest.schema,
materialProofs: canonicalProofs,
}),
)
.digest('hex'),
});
}
@@ -0,0 +1,140 @@
import { Buffer } from 'node:buffer';
import type { ToolInvocationArtifactKeyProvider } from '@qinglong/runtime-core/tool-invocation-artifact';
import { PrivateProjectedFileReader } from '../../security/privateProjectedFile';
import {
MAX_CLUSTER_TOOL_RESULT_KEYRING_BYTES,
canonicalClusterToolResultKeyringManifest,
parseClusterToolResultKeyringManifest,
resolveClusterToolResultKeyringMaterial,
summarizeClusterToolResultKeyringManifest,
type ClusterToolResultKeyringManifest,
type ClusterToolResultKeyringSummary,
} from './toolResultKeyringManifest';
const DATA_FILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,252}$/;
export {
CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA,
MAX_CLUSTER_TOOL_RESULT_KEYRING_BYTES,
MAX_CLUSTER_TOOL_RESULT_PROJECTED_KEYS,
InvalidClusterToolResultKeyringManifestError,
canonicalClusterToolResultKeyringManifest,
normalizeClusterToolResultKeyringManifest,
parseClusterToolResultKeyringManifest,
resolveClusterToolResultKeyringMaterial,
summarizeClusterToolResultKeyringManifest,
type ClusterToolResultKeyringManifest,
type ClusterToolResultKeyringSummary,
} from './toolResultKeyringManifest';
export interface ClusterToolResultProjectedKeyringOptions {
readonly rootDirectory: string;
readonly dataFileName?: string;
}
export class ClusterToolResultProjectedKeyringUnavailableError extends Error {
readonly code = 'QL3_CLUSTER_TOOL_RESULT_PROJECTED_KEYRING_UNAVAILABLE';
constructor(options?: ErrorOptions) {
super('Projected Cluster Tool result keyring is unavailable', options);
this.name = 'ClusterToolResultProjectedKeyringUnavailableError';
}
}
function unavailable(
cause?: unknown,
): ClusterToolResultProjectedKeyringUnavailableError {
return new ClusterToolResultProjectedKeyringUnavailableError({
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<ClusterToolResultKeyringManifest>> {
let bytes: Buffer | undefined;
let canonical: Buffer | undefined;
try {
bytes = await reader.read(fileName);
const manifest = parseClusterToolResultKeyringManifest(bytes);
canonical = canonicalClusterToolResultKeyringManifest(manifest);
if (!canonical.equals(bytes)) throw unavailable();
return manifest;
} catch (cause) {
throw cause instanceof ClusterToolResultProjectedKeyringUnavailableError
? cause
: unavailable(cause);
} finally {
bytes?.fill(0);
canonical?.fill(0);
}
}
/**
* Read-only result-key material authority for Kubernetes/CSI projections.
* PostgreSQL remains the sole active/decryptable state authority: this class
* only resolves key material and deliberately has no active() method.
*/
export class ClusterToolResultProjectedKeyring
implements Pick<ToolInvocationArtifactKeyProvider, 'resolve'>
{
readonly #reader: PrivateProjectedFileReader;
readonly #dataFileName: string;
constructor(options: ClusterToolResultProjectedKeyringOptions) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw unavailable();
}
try {
this.#reader = new PrivateProjectedFileReader({
rootDirectory: options.rootDirectory,
minimumBytes: 1,
maximumBytes: MAX_CLUSTER_TOOL_RESULT_KEYRING_BYTES,
access: 'read_only_keyring',
});
this.#dataFileName = dataFileName(options.dataFileName ?? 'keyring.json');
} catch (cause) {
throw unavailable(cause);
}
}
async verify(): Promise<Readonly<ClusterToolResultKeyringSummary>> {
return summarizeClusterToolResultKeyringManifest(
await readManifest(this.#reader, this.#dataFileName),
);
}
async resolve(
keyId: string,
): ReturnType<Pick<ToolInvocationArtifactKeyProvider, 'resolve'>['resolve']> {
try {
return resolveClusterToolResultKeyringMaterial(
await readManifest(this.#reader, this.#dataFileName),
keyId,
);
} catch (cause) {
throw cause instanceof ClusterToolResultProjectedKeyringUnavailableError
? cause
: unavailable(cause);
}
}
}
export async function createClusterToolResultProjectedKeyring(
options: ClusterToolResultProjectedKeyringOptions,
): Promise<Readonly<ClusterToolResultProjectedKeyring>> {
const provider = new ClusterToolResultProjectedKeyring(options);
await provider.verify();
return provider;
}
@@ -0,0 +1,193 @@
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 {
CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA,
ClusterToolResultProjectedKeyring,
ClusterToolResultProjectedKeyringUnavailableError,
canonicalClusterToolResultKeyringManifest,
createClusterToolResultProjectedKeyring,
} = require('../dist/trusted-tool/key-management/toolResultProjectedKeyring.js');
const {
toolResultKeyMaterialProof,
} = require('@qinglong/runtime-core/tool-result-key-catalog');
const roots = [];
afterEach(async () => {
await Promise.all(
roots
.splice(0)
.map((root) => fs.rm(root, { recursive: true, force: true })),
);
});
function manifest(keys) {
return Object.freeze({
schema: CLUSTER_TOOL_RESULT_KEYRING_MANIFEST_SCHEMA,
keys: Object.freeze(keys),
});
}
async function tempRoot() {
const root = await fs.mkdtemp(
path.join(os.tmpdir(), 'ql3-tool-result-keyring-'),
);
roots.push(root);
return root;
}
async function publish(root, generationName, bytes, 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, bytes, { 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 Tool result keyring follows atomic rotation without choosing the active catalog key', async () => {
const root = await tempRoot();
const keyOneBytes = Buffer.alloc(32, 0x11);
const keyTwoBytes = Buffer.alloc(32, 0x22);
const keyOne = keyOneBytes.toString('base64url');
const keyTwo = keyTwoBytes.toString('base64url');
await publish(
root,
'..2026_08_14_01',
canonicalClusterToolResultKeyringManifest(
manifest({ 'result-key-one': keyOne }),
),
);
const provider = await createClusterToolResultProjectedKeyring({
rootDirectory: root,
});
assert.equal('active' in provider, false);
const firstSummary = await provider.verify();
assert.deepEqual(firstSummary.keyIds, ['result-key-one']);
assert.equal(
firstSummary.materialProofs['result-key-one'],
toolResultKeyMaterialProof('result-key-one', keyOneBytes),
);
const first = await provider.resolve('result-key-one');
assert.ok(first);
assert.deepEqual(Buffer.from(first.key), keyOneBytes);
first.key.fill(0);
await publish(
root,
'..2026_08_14_02',
canonicalClusterToolResultKeyringManifest(
manifest({
'result-key-two': keyTwo,
'result-key-one': keyOne,
}),
),
);
const second = await provider.resolve('result-key-two');
assert.ok(second);
assert.deepEqual(Buffer.from(second.key), keyTwoBytes);
second.key.fill(0);
assert.equal(await provider.resolve('result-key-missing'), null);
});
test('projected Tool result keyring rejects noncanonical, writable and escaped material', async () => {
const encoded = Buffer.alloc(32, 0x33).toString('base64url');
const value = manifest({ 'result-key-one': encoded });
const noncanonicalRoot = await tempRoot();
await publish(
noncanonicalRoot,
'..2026_08_14_01',
Buffer.from(`${JSON.stringify(value, null, 2)}\n`, 'utf8'),
);
await assert.rejects(
() =>
createClusterToolResultProjectedKeyring({
rootDirectory: noncanonicalRoot,
}),
ClusterToolResultProjectedKeyringUnavailableError,
);
const writableRoot = await tempRoot();
await publish(
writableRoot,
'..2026_08_14_01',
canonicalClusterToolResultKeyringManifest(value),
0o640,
);
await assert.rejects(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: writableRoot,
}).verify(),
ClusterToolResultProjectedKeyringUnavailableError,
);
const escapedRoot = await tempRoot();
const externalRoot = await tempRoot();
const external = path.join(externalRoot, 'keyring.json');
await fs.writeFile(
external,
canonicalClusterToolResultKeyringManifest(value),
{ mode: 0o440 },
);
await fs.symlink(external, path.join(escapedRoot, 'keyring.json'));
await assert.rejects(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: escapedRoot,
}).verify(),
ClusterToolResultProjectedKeyringUnavailableError,
);
});
test('projected Tool result keyring rejects invalid material and ambiguous paths', async () => {
const invalidRoot = await tempRoot();
await publish(
invalidRoot,
'..2026_08_14_01',
Buffer.from(
`${JSON.stringify(
manifest({ 'result-key-one': Buffer.alloc(31).toString('base64url') }),
)}\n`,
'utf8',
),
);
await assert.rejects(
() =>
createClusterToolResultProjectedKeyring({ rootDirectory: invalidRoot }),
ClusterToolResultProjectedKeyringUnavailableError,
);
const root = await tempRoot();
const rootLink = `${root}-link`;
roots.push(rootLink);
await fs.symlink(root, rootLink);
await assert.rejects(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: rootLink,
}).verify(),
ClusterToolResultProjectedKeyringUnavailableError,
);
assert.throws(
() =>
new ClusterToolResultProjectedKeyring({
rootDirectory: root,
dataFileName: '../keyring.json',
}),
ClusterToolResultProjectedKeyringUnavailableError,
);
});