hololake-system-architecture/product-source/guanghu-knowledge-base/server/domain-runtime-handoff-source.ts

206 lines
8.3 KiB
TypeScript

import type {
DomainRuntimeHandoffCandidate,
DomainRuntimeHandoffSource,
} from './domain-access-orchestrator.js';
import {
assertVerifiedNodePossession,
type NodePossessionChallenge,
type NodePossessionResponse,
type VerifiedNodePossession,
} from './node-possession-proof.js';
import type { VerifiedNodeRegistration } from './node-registration.js';
import { randomUUID } from 'node:crypto';
import {
assertRegisteredDomainRuntimeHandoffEndpoint,
resolveDomainRuntimeHandoffEndpoint,
type DomainRuntimeHandoffEndpoint,
} from './domain-runtime-handoff-registry.js';
import {
assertLoadedDomainRuntimeHandoffSnapshot,
type DomainRuntimeHandoffSnapshotLoader,
} from './domain-runtime-handoff-snapshot.js';
const IDENTIFIER_PATTERN = /^[A-Z0-9][A-Z0-9._:-]{1,159}$/;
const REQUEST_SCHEMA = 'gh-aios.domain-runtime-handoff-request/v1' as const;
const RESPONSE_SCHEMA = 'gh-aios.domain-runtime-handoff-response/v1' as const;
const consumedPossessions = new WeakSet<object>();
const MAX_RESPONSE_BYTES = 256 * 1024;
export interface DomainRuntimeHandoffRequest {
accountId: string;
challenge: Readonly<NodePossessionChallenge>;
domainId: string;
endpointId: string;
issuedAt: number;
nodeId: string;
nodeType: VerifiedNodeRegistration['nodeType'];
registration: Readonly<VerifiedNodeRegistration>;
requestId: string;
response: Readonly<NodePossessionResponse>;
schema: typeof REQUEST_SCHEMA;
}
export interface DomainRuntimeHandoffTransport {
request(
endpoint: Readonly<DomainRuntimeHandoffEndpoint>,
input: Readonly<DomainRuntimeHandoffRequest>,
): Promise<unknown>;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function hasExactKeys(value: Record<string, unknown>, expected: readonly string[]): boolean {
const actual = Object.keys(value).sort();
const keys = [...expected].sort();
return actual.length === keys.length && actual.every((key, index) => key === keys[index]);
}
function parseResponse(
input: unknown,
request: DomainRuntimeHandoffRequest,
): Readonly<DomainRuntimeHandoffCandidate> | null {
if (!isRecord(input)
|| !hasExactKeys(input, ['handoff', 'request_id', 'schema', 'signer_lookup', 'status'])
|| input.schema !== RESPONSE_SCHEMA
|| input.request_id !== request.requestId
|| (input.status !== 'ISSUED' && input.status !== 'NOT_AUTHORIZED')) {
return null;
}
if (input.status === 'NOT_AUTHORIZED') {
if (input.handoff !== null || input.signer_lookup !== null) return null;
return null;
}
if (!isRecord(input.handoff)
|| !isRecord(input.signer_lookup)
|| !hasExactKeys(input.signer_lookup, ['repository_id', 'signer_id'])
|| typeof input.signer_lookup.repository_id !== 'string'
|| !IDENTIFIER_PATTERN.test(input.signer_lookup.repository_id)
|| typeof input.signer_lookup.signer_id !== 'string'
|| !IDENTIFIER_PATTERN.test(input.signer_lookup.signer_id)) return null;
return Object.freeze({
handoff: input.handoff,
signerLookup: Object.freeze({
repositoryId: input.signer_lookup.repository_id,
signerId: input.signer_lookup.signer_id,
}),
});
}
export class StrictDomainRuntimeHandoffSource implements DomainRuntimeHandoffSource {
constructor(
private readonly endpoint: DomainRuntimeHandoffEndpoint,
private readonly transport: DomainRuntimeHandoffTransport,
private readonly clock: () => number = Date.now,
private readonly requestId: () => string = () => `HANDOFF-REQ-${randomUUID().toUpperCase()}`,
) {}
async read(possession: VerifiedNodePossession): Promise<Readonly<DomainRuntimeHandoffCandidate> | null> {
try {
const verified = assertVerifiedNodePossession(possession);
if (consumedPossessions.has(verified)) return null;
const endpoint = assertRegisteredDomainRuntimeHandoffEndpoint(this.endpoint);
if (endpoint.status !== 'ACTIVE'
|| !endpoint.domainIds.includes(verified.registration.domainId)
|| !endpoint.nodeTypes.includes(verified.registration.nodeType)) return null;
const issuedAt = this.clock();
const requestId = this.requestId();
if (!Number.isSafeInteger(issuedAt)
|| issuedAt < verified.verifiedAt
|| issuedAt >= verified.challenge.expiresAt
|| issuedAt >= verified.registration.expiresAt
|| !IDENTIFIER_PATTERN.test(requestId)) return null;
const request = Object.freeze({
accountId: verified.registration.accountId,
challenge: verified.challenge,
domainId: verified.registration.domainId,
endpointId: endpoint.endpointId,
issuedAt,
nodeId: verified.registration.nodeId,
nodeType: verified.registration.nodeType,
registration: verified.registration,
requestId,
response: verified.response,
schema: REQUEST_SCHEMA,
});
consumedPossessions.add(verified);
return parseResponse(await this.transport.request(endpoint, request), request);
} catch {
return null;
}
}
}
export class AnchoredDomainRuntimeHandoffSource implements DomainRuntimeHandoffSource {
constructor(
private readonly snapshots: DomainRuntimeHandoffSnapshotLoader,
private readonly transport: DomainRuntimeHandoffTransport,
private readonly clock: () => number = Date.now,
private readonly requestId: () => string = () => `HANDOFF-REQ-${randomUUID().toUpperCase()}`,
) {}
async read(possession: VerifiedNodePossession): Promise<Readonly<DomainRuntimeHandoffCandidate> | null> {
try {
const verified = assertVerifiedNodePossession(possession);
const snapshot = assertLoadedDomainRuntimeHandoffSnapshot(await this.snapshots.refresh());
if (snapshot.receipt.status !== 'CURRENT' || !snapshot.registry) return null;
const endpoint = resolveDomainRuntimeHandoffEndpoint(snapshot.registry, {
domainId: verified.registration.domainId,
nodeType: verified.registration.nodeType,
});
if (!endpoint) return null;
return new StrictDomainRuntimeHandoffSource(
endpoint,
this.transport,
this.clock,
this.requestId,
).read(verified);
} catch {
return null;
}
}
}
export class HttpDomainRuntimeHandoffTransport implements DomainRuntimeHandoffTransport {
constructor(private readonly fetcher: typeof fetch = fetch) {}
async request(
inputEndpoint: Readonly<DomainRuntimeHandoffEndpoint>,
input: Readonly<DomainRuntimeHandoffRequest>,
): Promise<unknown> {
const endpoint = assertRegisteredDomainRuntimeHandoffEndpoint(inputEndpoint);
if (endpoint.status !== 'ACTIVE'
|| input.endpointId !== endpoint.endpointId
|| !endpoint.domainIds.includes(input.domainId)
|| !endpoint.nodeTypes.includes(input.nodeType)) {
throw new Error('domain_runtime_handoff_endpoint_scope_invalid');
}
const response = await this.fetcher(endpoint.url, {
body: JSON.stringify(input),
cache: 'no-store',
credentials: 'omit',
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
method: 'POST',
redirect: 'manual',
referrerPolicy: 'no-referrer',
signal: AbortSignal.timeout(5000),
});
if (!response.ok || response.status < 200 || response.status >= 300
|| response.type === 'opaqueredirect') throw new Error('domain_runtime_handoff_http_rejected');
const contentType = response.headers.get('content-type')?.split(';', 1)[0].trim().toLowerCase();
if (contentType !== 'application/json') throw new Error('domain_runtime_handoff_content_type_invalid');
const declaredLength = response.headers.get('content-length');
if (declaredLength !== null
&& (!/^\d+$/.test(declaredLength) || Number(declaredLength) > MAX_RESPONSE_BYTES)) {
throw new Error('domain_runtime_handoff_response_too_large');
}
const bytes = new Uint8Array(await response.arrayBuffer());
if (bytes.byteLength > MAX_RESPONSE_BYTES) throw new Error('domain_runtime_handoff_response_too_large');
let text: string;
try { text = new TextDecoder('utf-8', { fatal: true }).decode(bytes); }
catch { throw new Error('domain_runtime_handoff_response_encoding_invalid'); }
try { return JSON.parse(text); }
catch { throw new Error('domain_runtime_handoff_response_json_invalid'); }
}
}