feat: anchor domain runtime handoff transport
This commit is contained in:
parent
7cb5de1a0e
commit
9994d81fd2
9 changed files with 759 additions and 19 deletions
|
|
@ -0,0 +1,66 @@
|
||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import {
|
||||||
|
assertRegisteredDomainRuntimeHandoffEndpoint,
|
||||||
|
parseDomainRuntimeHandoffEndpointRegistry,
|
||||||
|
resolveDomainRuntimeHandoffEndpoint,
|
||||||
|
} from './domain-runtime-handoff-registry.js';
|
||||||
|
|
||||||
|
const COMMIT = 'a'.repeat(40);
|
||||||
|
const SOURCE = {
|
||||||
|
repositoryId: 'REPO-012' as const,
|
||||||
|
sourceCommit: COMMIT,
|
||||||
|
sourceUrl: `https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/commit/${COMMIT}/routing/domain-runtime-handoff-endpoints.json`,
|
||||||
|
};
|
||||||
|
|
||||||
|
function endpoint(overrides: Record<string, unknown> = {}) {
|
||||||
|
return {
|
||||||
|
domainIds: ['DOM-FIFTH-0001'], endpointId: 'GH-DOMAIN-HANDOFF-001',
|
||||||
|
nodeTypes: ['cloud-resident'], status: 'ACTIVE',
|
||||||
|
url: 'https://guanghulab.com/api/ai/v1/domain-runtime/handoff', ...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function registry(endpoints: unknown[] = [endpoint()]) {
|
||||||
|
return {
|
||||||
|
endpoints, registryId: 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001',
|
||||||
|
schema: 'gh-aios.domain-runtime-handoff-endpoints/v1', state: 'CURRENT', version: '1.0.0',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('parses and resolves one anchored endpoint for the exact scope', () => {
|
||||||
|
const parsed = parseDomainRuntimeHandoffEndpointRegistry(registry(), SOURCE);
|
||||||
|
const resolved = resolveDomainRuntimeHandoffEndpoint(parsed, {
|
||||||
|
domainId: 'DOM-FIFTH-0001', nodeType: 'cloud-resident',
|
||||||
|
});
|
||||||
|
assert.ok(resolved);
|
||||||
|
assert.equal(resolved.url, 'https://guanghulab.com/api/ai/v1/domain-runtime/handoff');
|
||||||
|
assert.equal(resolveDomainRuntimeHandoffEndpoint(parsed, {
|
||||||
|
domainId: 'DOM-FIFTH-0001', nodeType: 'local-terminal',
|
||||||
|
}), null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects unsafe URLs, unknown fields and duplicate active scopes', () => {
|
||||||
|
for (const unsafe of [
|
||||||
|
'http://guanghulab.com/api/ai/v1/handoff',
|
||||||
|
'https://example.com/api/ai/v1/handoff',
|
||||||
|
'https://user@guanghulab.com/api/ai/v1/handoff',
|
||||||
|
'https://guanghulab.com:8443/api/ai/v1/handoff',
|
||||||
|
'https://guanghulab.com/api/ai/v1/handoff?target=other',
|
||||||
|
]) {
|
||||||
|
assert.throws(() => parseDomainRuntimeHandoffEndpointRegistry(registry([endpoint({ url: unsafe })]), SOURCE));
|
||||||
|
}
|
||||||
|
assert.throws(() => parseDomainRuntimeHandoffEndpointRegistry(registry([endpoint({ extra: true })]), SOURCE));
|
||||||
|
assert.throws(() => parseDomainRuntimeHandoffEndpointRegistry(registry([
|
||||||
|
endpoint(), endpoint({ endpointId: 'GH-DOMAIN-HANDOFF-002' }),
|
||||||
|
]), SOURCE));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects caller-created and copied endpoint objects', () => {
|
||||||
|
const parsed = parseDomainRuntimeHandoffEndpointRegistry(registry(), SOURCE);
|
||||||
|
const resolved = resolveDomainRuntimeHandoffEndpoint(parsed, {
|
||||||
|
domainId: 'DOM-FIFTH-0001', nodeType: 'cloud-resident',
|
||||||
|
});
|
||||||
|
assert.ok(resolved);
|
||||||
|
assert.throws(() => assertRegisteredDomainRuntimeHandoffEndpoint({ ...resolved }), /endpoint_unregistered/);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
import type { DomainNodeType } from './domain-access.js';
|
||||||
|
|
||||||
|
const REGISTRY_SCHEMA = 'gh-aios.domain-runtime-handoff-endpoints/v1' as const;
|
||||||
|
const IDENTIFIER_PATTERN = /^[A-Z0-9][A-Z0-9._:-]{1,159}$/;
|
||||||
|
const COMMIT_PATTERN = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/;
|
||||||
|
const VERSION_PATTERN = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
|
||||||
|
const registeredEndpoints = new WeakSet<object>();
|
||||||
|
|
||||||
|
export interface DomainRuntimeHandoffEndpoint {
|
||||||
|
domainIds: readonly string[];
|
||||||
|
endpointId: string;
|
||||||
|
nodeTypes: readonly DomainNodeType[];
|
||||||
|
status: 'ACTIVE' | 'REVOKED';
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DomainRuntimeHandoffEndpointRegistrySource {
|
||||||
|
repositoryId: 'REPO-012';
|
||||||
|
sourceCommit: string;
|
||||||
|
sourceUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DomainRuntimeHandoffEndpointRegistry {
|
||||||
|
endpoints: readonly DomainRuntimeHandoffEndpoint[];
|
||||||
|
registryId: 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001';
|
||||||
|
schema: typeof REGISTRY_SCHEMA;
|
||||||
|
source: Readonly<DomainRuntimeHandoffEndpointRegistrySource>;
|
||||||
|
state: 'CURRENT';
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DomainRuntimeHandoffEndpointLookup {
|
||||||
|
domainId: string;
|
||||||
|
nodeType: DomainNodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidRegistry(): never {
|
||||||
|
throw new Error('domain_runtime_handoff_endpoint_registry_invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasExactKeys(record: Record<string, unknown>, keys: string[]): boolean {
|
||||||
|
const expected = [...keys].sort();
|
||||||
|
const actual = Object.keys(record).sort();
|
||||||
|
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIdentifier(value: unknown): value is string {
|
||||||
|
return typeof value === 'string' && IDENTIFIER_PATTERN.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEndpointUrl(value: unknown): string {
|
||||||
|
if (typeof value !== 'string') invalidRegistry();
|
||||||
|
let url: URL;
|
||||||
|
try {
|
||||||
|
url = new URL(value);
|
||||||
|
} catch {
|
||||||
|
invalidRegistry();
|
||||||
|
}
|
||||||
|
if (url.protocol !== 'https:'
|
||||||
|
|| url.hostname !== 'guanghulab.com'
|
||||||
|
|| url.port
|
||||||
|
|| url.username
|
||||||
|
|| url.password
|
||||||
|
|| url.search
|
||||||
|
|| url.hash
|
||||||
|
|| !url.pathname.startsWith('/api/ai/v1/')) invalidRegistry();
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEndpoint(input: unknown): DomainRuntimeHandoffEndpoint {
|
||||||
|
if (!isRecord(input)
|
||||||
|
|| !hasExactKeys(input, ['domainIds', 'endpointId', 'nodeTypes', 'status', 'url'])
|
||||||
|
|| !isIdentifier(input.endpointId)
|
||||||
|
|| (input.status !== 'ACTIVE' && input.status !== 'REVOKED')
|
||||||
|
|| !Array.isArray(input.domainIds)
|
||||||
|
|| input.domainIds.length === 0
|
||||||
|
|| input.domainIds.some(domainId => !isIdentifier(domainId))
|
||||||
|
|| new Set(input.domainIds).size !== input.domainIds.length
|
||||||
|
|| !Array.isArray(input.nodeTypes)
|
||||||
|
|| input.nodeTypes.length === 0
|
||||||
|
|| input.nodeTypes.some(nodeType => nodeType !== 'local-terminal' && nodeType !== 'cloud-resident')
|
||||||
|
|| new Set(input.nodeTypes).size !== input.nodeTypes.length) invalidRegistry();
|
||||||
|
const endpoint = Object.freeze({
|
||||||
|
domainIds: Object.freeze([...input.domainIds] as string[]),
|
||||||
|
endpointId: input.endpointId,
|
||||||
|
nodeTypes: Object.freeze([...input.nodeTypes] as DomainNodeType[]),
|
||||||
|
status: input.status,
|
||||||
|
url: parseEndpointUrl(input.url),
|
||||||
|
});
|
||||||
|
registeredEndpoints.add(endpoint);
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSource(
|
||||||
|
source: DomainRuntimeHandoffEndpointRegistrySource,
|
||||||
|
): Readonly<DomainRuntimeHandoffEndpointRegistrySource> {
|
||||||
|
if (!isRecord(source)
|
||||||
|
|| !hasExactKeys(source, ['repositoryId', 'sourceCommit', 'sourceUrl'])
|
||||||
|
|| source.repositoryId !== 'REPO-012'
|
||||||
|
|| typeof source.sourceCommit !== 'string'
|
||||||
|
|| !COMMIT_PATTERN.test(source.sourceCommit)) invalidRegistry();
|
||||||
|
const expected = `https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/commit/${source.sourceCommit}/routing/domain-runtime-handoff-endpoints.json`;
|
||||||
|
if (source.sourceUrl !== expected) invalidRegistry();
|
||||||
|
return Object.freeze({ ...source });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseDomainRuntimeHandoffEndpointRegistry(
|
||||||
|
input: unknown,
|
||||||
|
source: DomainRuntimeHandoffEndpointRegistrySource,
|
||||||
|
): DomainRuntimeHandoffEndpointRegistry {
|
||||||
|
if (!isRecord(input)
|
||||||
|
|| !hasExactKeys(input, ['endpoints', 'registryId', 'schema', 'state', 'version'])
|
||||||
|
|| input.registryId !== 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001'
|
||||||
|
|| input.schema !== REGISTRY_SCHEMA
|
||||||
|
|| input.state !== 'CURRENT'
|
||||||
|
|| typeof input.version !== 'string'
|
||||||
|
|| !VERSION_PATTERN.test(input.version)
|
||||||
|
|| !Array.isArray(input.endpoints)) invalidRegistry();
|
||||||
|
const endpoints = input.endpoints.map(parseEndpoint);
|
||||||
|
if (new Set(endpoints.map(endpoint => endpoint.endpointId)).size !== endpoints.length) invalidRegistry();
|
||||||
|
const activeScopes = new Set<string>();
|
||||||
|
for (const endpoint of endpoints.filter(candidate => candidate.status === 'ACTIVE')) {
|
||||||
|
for (const domainId of endpoint.domainIds) {
|
||||||
|
for (const nodeType of endpoint.nodeTypes) {
|
||||||
|
const scope = `${domainId}\0${nodeType}`;
|
||||||
|
if (activeScopes.has(scope)) invalidRegistry();
|
||||||
|
activeScopes.add(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.freeze({
|
||||||
|
endpoints: Object.freeze(endpoints),
|
||||||
|
registryId: input.registryId,
|
||||||
|
schema: input.schema,
|
||||||
|
source: parseSource(source),
|
||||||
|
state: input.state,
|
||||||
|
version: input.version,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveDomainRuntimeHandoffEndpoint(
|
||||||
|
registry: DomainRuntimeHandoffEndpointRegistry,
|
||||||
|
lookup: DomainRuntimeHandoffEndpointLookup,
|
||||||
|
): DomainRuntimeHandoffEndpoint | null {
|
||||||
|
const endpoint = registry.endpoints.find(candidate => candidate.status === 'ACTIVE'
|
||||||
|
&& candidate.domainIds.includes(lookup.domainId)
|
||||||
|
&& candidate.nodeTypes.includes(lookup.nodeType));
|
||||||
|
if (!endpoint) return null;
|
||||||
|
return assertRegisteredDomainRuntimeHandoffEndpoint(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertRegisteredDomainRuntimeHandoffEndpoint(
|
||||||
|
endpoint: DomainRuntimeHandoffEndpoint,
|
||||||
|
): DomainRuntimeHandoffEndpoint {
|
||||||
|
if (!registeredEndpoints.has(endpoint)) throw new Error('domain_runtime_handoff_endpoint_unregistered');
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import {
|
||||||
|
assertLoadedDomainRuntimeHandoffSnapshot,
|
||||||
|
DomainRuntimeHandoffSnapshotLoader,
|
||||||
|
} from './domain-runtime-handoff-snapshot.js';
|
||||||
|
|
||||||
|
const COMMIT = 'a'.repeat(40);
|
||||||
|
const ANCHOR_URL = 'https://guanghulab.com/api/ai/v1/anchor';
|
||||||
|
const REGISTRY_URL = `https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/commit/${COMMIT}/routing/domain-runtime-handoff-endpoints.json`;
|
||||||
|
|
||||||
|
function anchor(includeMap = true) {
|
||||||
|
return {
|
||||||
|
schema: 'guanghu.public-navigation-anchor/v1', anchor_id: 'GLW-PUBLIC-NAV-ANCHOR-001',
|
||||||
|
state: 'CURRENT_CANONICAL', repository_id: 'REPO-012', branch: 'main', public_entry: ANCHOR_URL,
|
||||||
|
code_entry: 'https://guanghulab.com/code/bingshuo/guanghu-ice-heart',
|
||||||
|
maps: includeMap ? { domain_runtime_handoff_endpoints: {
|
||||||
|
path: 'routing/domain-runtime-handoff-endpoints.json',
|
||||||
|
id: 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001', version: '1.0.0',
|
||||||
|
} } : {},
|
||||||
|
navigation_source: { anchor_id: 'GLW-PUBLIC-NAV-ANCHOR-001', source_commit: COMMIT,
|
||||||
|
source_mode: 'REPO-012_MAIN_GIT_SNAPSHOT', source_degraded: false },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function registry(version = '1.0.0') {
|
||||||
|
return {
|
||||||
|
endpoints: [{ domainIds: ['DOM-FIFTH-0001'], endpointId: 'GH-DOMAIN-HANDOFF-001',
|
||||||
|
nodeTypes: ['cloud-resident'], status: 'ACTIVE',
|
||||||
|
url: 'https://guanghulab.com/api/ai/v1/domain-runtime/handoff' }],
|
||||||
|
registryId: 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001',
|
||||||
|
schema: 'gh-aios.domain-runtime-handoff-endpoints/v1', state: 'CURRENT', version,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function loader(readAnchor: () => unknown, readRegistry: () => unknown) {
|
||||||
|
return new DomainRuntimeHandoffSnapshotLoader({
|
||||||
|
async fetchJson(url) {
|
||||||
|
if (url === ANCHOR_URL) return readAnchor();
|
||||||
|
if (url === REGISTRY_URL) return readRegistry();
|
||||||
|
throw new Error('unexpected_url');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test('loads only the registry at the exact anchor source commit', async () => {
|
||||||
|
const snapshot = await loader(() => anchor(), () => registry()).refresh();
|
||||||
|
assert.equal(snapshot.receipt.status, 'CURRENT');
|
||||||
|
assert.equal(snapshot.receipt.sourceCommit, COMMIT);
|
||||||
|
assert.equal(snapshot.receipt.endpointCount, 1);
|
||||||
|
assert.equal(assertLoadedDomainRuntimeHandoffSnapshot(snapshot), snapshot);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current anchor without the handoff map is explicitly unavailable', async () => {
|
||||||
|
const snapshot = await loader(() => anchor(false), () => registry()).refresh();
|
||||||
|
assert.deepEqual(snapshot.receipt, { endpointCount: 0, reason: 'REGISTRY_UNREGISTERED',
|
||||||
|
registryVersion: null, sourceCommit: null, status: 'UNAVAILABLE' });
|
||||||
|
assert.equal(snapshot.registry, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('invalid and degraded sources remain unusable as current authority', async () => {
|
||||||
|
const invalid = await loader(() => anchor(), () => registry('2.0.0')).refresh();
|
||||||
|
assert.equal(invalid.receipt.reason, 'REGISTRY_INVALID');
|
||||||
|
let currentAnchor: unknown = anchor();
|
||||||
|
const snapshots = loader(() => currentAnchor, () => registry());
|
||||||
|
await snapshots.refresh();
|
||||||
|
currentAnchor = anchor(false);
|
||||||
|
const degraded = await snapshots.refresh();
|
||||||
|
assert.equal(degraded.receipt.status, 'DEGRADED_LAST_KNOWN_GOOD');
|
||||||
|
assert.equal(degraded.receipt.reason, 'REGISTRY_UNREGISTERED');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('caller-created snapshots are rejected', () => {
|
||||||
|
assert.throws(() => assertLoadedDomainRuntimeHandoffSnapshot({
|
||||||
|
receipt: { endpointCount: 0, registryVersion: null, sourceCommit: null, status: 'UNAVAILABLE' },
|
||||||
|
registry: null,
|
||||||
|
}), /snapshot_unregistered/);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
import {
|
||||||
|
parseDomainRuntimeHandoffEndpointRegistry,
|
||||||
|
type DomainRuntimeHandoffEndpointRegistry,
|
||||||
|
} from './domain-runtime-handoff-registry.js';
|
||||||
|
|
||||||
|
const ANCHOR_URL = 'https://guanghulab.com/api/ai/v1/anchor';
|
||||||
|
const CODE_URL = 'https://guanghulab.com/code/bingshuo/guanghu-ice-heart';
|
||||||
|
const REGISTRY_ID = 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001';
|
||||||
|
const REGISTRY_PATH = 'routing/domain-runtime-handoff-endpoints.json';
|
||||||
|
const COMMIT_PATTERN = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/;
|
||||||
|
const VERSION_PATTERN = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
|
||||||
|
const registeredSnapshots = new WeakSet<object>();
|
||||||
|
|
||||||
|
export interface DomainRuntimeHandoffSnapshotTransport {
|
||||||
|
fetchJson(url: string): Promise<unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DomainRuntimeHandoffSnapshotReason =
|
||||||
|
| 'ANCHOR_INVALID'
|
||||||
|
| 'REGISTRY_INVALID'
|
||||||
|
| 'REGISTRY_UNREGISTERED'
|
||||||
|
| 'SOURCE_UNAVAILABLE';
|
||||||
|
|
||||||
|
export interface DomainRuntimeHandoffSnapshotReceipt {
|
||||||
|
endpointCount: number;
|
||||||
|
reason?: DomainRuntimeHandoffSnapshotReason;
|
||||||
|
registryVersion: string | null;
|
||||||
|
sourceCommit: string | null;
|
||||||
|
status: 'CURRENT' | 'DEGRADED_LAST_KNOWN_GOOD' | 'UNAVAILABLE';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DomainRuntimeHandoffSnapshotResult {
|
||||||
|
receipt: Readonly<DomainRuntimeHandoffSnapshotReceipt>;
|
||||||
|
registry: DomainRuntimeHandoffEndpointRegistry | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParsedAnchor { registryVersion: string; sourceCommit: string }
|
||||||
|
|
||||||
|
class SnapshotLoadError extends Error {
|
||||||
|
constructor(readonly reason: DomainRuntimeHandoffSnapshotReason) { super(reason); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseAnchor(input: unknown): ParsedAnchor {
|
||||||
|
if (!isRecord(input)
|
||||||
|
|| input.schema !== 'guanghu.public-navigation-anchor/v1'
|
||||||
|
|| input.anchor_id !== 'GLW-PUBLIC-NAV-ANCHOR-001'
|
||||||
|
|| input.state !== 'CURRENT_CANONICAL'
|
||||||
|
|| input.repository_id !== 'REPO-012'
|
||||||
|
|| input.branch !== 'main'
|
||||||
|
|| input.public_entry !== ANCHOR_URL
|
||||||
|
|| input.code_entry !== CODE_URL
|
||||||
|
|| !isRecord(input.maps)
|
||||||
|
|| !isRecord(input.navigation_source)
|
||||||
|
|| input.navigation_source.anchor_id !== 'GLW-PUBLIC-NAV-ANCHOR-001'
|
||||||
|
|| input.navigation_source.source_mode !== 'REPO-012_MAIN_GIT_SNAPSHOT'
|
||||||
|
|| input.navigation_source.source_degraded !== false
|
||||||
|
|| typeof input.navigation_source.source_commit !== 'string'
|
||||||
|
|| !COMMIT_PATTERN.test(input.navigation_source.source_commit)) {
|
||||||
|
throw new SnapshotLoadError('ANCHOR_INVALID');
|
||||||
|
}
|
||||||
|
if (!isRecord(input.maps.domain_runtime_handoff_endpoints)) {
|
||||||
|
throw new SnapshotLoadError('REGISTRY_UNREGISTERED');
|
||||||
|
}
|
||||||
|
const map = input.maps.domain_runtime_handoff_endpoints;
|
||||||
|
if (map.path !== REGISTRY_PATH
|
||||||
|
|| map.id !== REGISTRY_ID
|
||||||
|
|| typeof map.version !== 'string'
|
||||||
|
|| !VERSION_PATTERN.test(map.version)) throw new SnapshotLoadError('ANCHOR_INVALID');
|
||||||
|
return Object.freeze({
|
||||||
|
registryVersion: map.version,
|
||||||
|
sourceCommit: input.navigation_source.source_commit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function registryUrl(sourceCommit: string): string {
|
||||||
|
return `${CODE_URL}/raw/commit/${sourceCommit}/${REGISTRY_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function receipt(
|
||||||
|
status: DomainRuntimeHandoffSnapshotReceipt['status'],
|
||||||
|
registry: DomainRuntimeHandoffEndpointRegistry | null,
|
||||||
|
reason?: DomainRuntimeHandoffSnapshotReason,
|
||||||
|
): Readonly<DomainRuntimeHandoffSnapshotReceipt> {
|
||||||
|
return Object.freeze({
|
||||||
|
endpointCount: registry?.endpoints.length ?? 0,
|
||||||
|
...(reason ? { reason } : {}),
|
||||||
|
registryVersion: registry?.version ?? null,
|
||||||
|
sourceCommit: registry?.source.sourceCommit ?? null,
|
||||||
|
status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function result(
|
||||||
|
registry: DomainRuntimeHandoffEndpointRegistry | null,
|
||||||
|
snapshotReceipt: Readonly<DomainRuntimeHandoffSnapshotReceipt>,
|
||||||
|
): DomainRuntimeHandoffSnapshotResult {
|
||||||
|
const loaded = Object.freeze({ receipt: snapshotReceipt, registry });
|
||||||
|
registeredSnapshots.add(loaded);
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertLoadedDomainRuntimeHandoffSnapshot(
|
||||||
|
snapshot: DomainRuntimeHandoffSnapshotResult,
|
||||||
|
): DomainRuntimeHandoffSnapshotResult {
|
||||||
|
if (!registeredSnapshots.has(snapshot)) throw new Error('domain_runtime_handoff_snapshot_unregistered');
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DomainRuntimeHandoffSnapshotLoader {
|
||||||
|
#inFlight: Promise<DomainRuntimeHandoffSnapshotResult> | null = null;
|
||||||
|
#lastKnownGood: DomainRuntimeHandoffEndpointRegistry | null = null;
|
||||||
|
|
||||||
|
constructor(private readonly transport: DomainRuntimeHandoffSnapshotTransport) {}
|
||||||
|
|
||||||
|
refresh(): Promise<DomainRuntimeHandoffSnapshotResult> {
|
||||||
|
if (this.#inFlight) return this.#inFlight;
|
||||||
|
this.#inFlight = this.#load().finally(() => { this.#inFlight = null; });
|
||||||
|
return this.#inFlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #load(): Promise<DomainRuntimeHandoffSnapshotResult> {
|
||||||
|
try {
|
||||||
|
let rawAnchor: unknown;
|
||||||
|
try { rawAnchor = await this.transport.fetchJson(ANCHOR_URL); }
|
||||||
|
catch { throw new SnapshotLoadError('SOURCE_UNAVAILABLE'); }
|
||||||
|
const anchor = parseAnchor(rawAnchor);
|
||||||
|
let rawRegistry: unknown;
|
||||||
|
try { rawRegistry = await this.transport.fetchJson(registryUrl(anchor.sourceCommit)); }
|
||||||
|
catch { throw new SnapshotLoadError('SOURCE_UNAVAILABLE'); }
|
||||||
|
let registry: DomainRuntimeHandoffEndpointRegistry;
|
||||||
|
try {
|
||||||
|
registry = parseDomainRuntimeHandoffEndpointRegistry(rawRegistry, {
|
||||||
|
repositoryId: 'REPO-012',
|
||||||
|
sourceCommit: anchor.sourceCommit,
|
||||||
|
sourceUrl: registryUrl(anchor.sourceCommit),
|
||||||
|
});
|
||||||
|
} catch { throw new SnapshotLoadError('REGISTRY_INVALID'); }
|
||||||
|
if (registry.version !== anchor.registryVersion) throw new SnapshotLoadError('REGISTRY_INVALID');
|
||||||
|
this.#lastKnownGood = registry;
|
||||||
|
return result(registry, receipt('CURRENT', registry));
|
||||||
|
} catch (error) {
|
||||||
|
const reason = error instanceof SnapshotLoadError ? error.reason : 'SOURCE_UNAVAILABLE';
|
||||||
|
return this.#lastKnownGood
|
||||||
|
? result(this.#lastKnownGood, receipt('DEGRADED_LAST_KNOWN_GOOD', this.#lastKnownGood, reason))
|
||||||
|
: result(null, receipt('UNAVAILABLE', null, reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,16 @@
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { createHash, generateKeyPairSync, sign } from 'node:crypto';
|
import { createHash, generateKeyPairSync, sign } from 'node:crypto';
|
||||||
import { StrictDomainRuntimeHandoffSource } from './domain-runtime-handoff-source.js';
|
import {
|
||||||
|
AnchoredDomainRuntimeHandoffSource,
|
||||||
|
HttpDomainRuntimeHandoffTransport,
|
||||||
|
StrictDomainRuntimeHandoffSource,
|
||||||
|
} from './domain-runtime-handoff-source.js';
|
||||||
|
import { DomainRuntimeHandoffSnapshotLoader } from './domain-runtime-handoff-snapshot.js';
|
||||||
|
import {
|
||||||
|
parseDomainRuntimeHandoffEndpointRegistry,
|
||||||
|
resolveDomainRuntimeHandoffEndpoint,
|
||||||
|
} from './domain-runtime-handoff-registry.js';
|
||||||
import {
|
import {
|
||||||
createNodePossessionChallenge,
|
createNodePossessionChallenge,
|
||||||
nodePossessionChallengeSigningBytes,
|
nodePossessionChallengeSigningBytes,
|
||||||
|
|
@ -25,6 +34,29 @@ const fingerprint = createHash('sha256')
|
||||||
.update(nodeKeys.publicKey.export({ format: 'der', type: 'spki' }))
|
.update(nodeKeys.publicKey.export({ format: 'der', type: 'spki' }))
|
||||||
.digest('hex');
|
.digest('hex');
|
||||||
|
|
||||||
|
function handoffEndpoint() {
|
||||||
|
const registry = parseDomainRuntimeHandoffEndpointRegistry({
|
||||||
|
endpoints: [{
|
||||||
|
domainIds: [DOMAIN_ID],
|
||||||
|
endpointId: 'GH-DOMAIN-HANDOFF-001',
|
||||||
|
nodeTypes: [NODE_TYPE],
|
||||||
|
status: 'ACTIVE',
|
||||||
|
url: 'https://guanghulab.com/api/ai/v1/domain-runtime/handoff',
|
||||||
|
}],
|
||||||
|
registryId: 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001',
|
||||||
|
schema: 'gh-aios.domain-runtime-handoff-endpoints/v1',
|
||||||
|
state: 'CURRENT',
|
||||||
|
version: '1.0.0',
|
||||||
|
}, {
|
||||||
|
repositoryId: 'REPO-012',
|
||||||
|
sourceCommit: 'b'.repeat(40),
|
||||||
|
sourceUrl: `https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/commit/${'b'.repeat(40)}/routing/domain-runtime-handoff-endpoints.json`,
|
||||||
|
});
|
||||||
|
const endpoint = resolveDomainRuntimeHandoffEndpoint(registry, { domainId: DOMAIN_ID, nodeType: NODE_TYPE });
|
||||||
|
assert.ok(endpoint);
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
function verifiedPossession() {
|
function verifiedPossession() {
|
||||||
const registry = parseNodeRegistrationEndpointRegistry({
|
const registry = parseNodeRegistrationEndpointRegistry({
|
||||||
endpoints: [{
|
endpoints: [{
|
||||||
|
|
@ -101,8 +133,8 @@ function response(requestId: string, overrides: Record<string, unknown> = {}) {
|
||||||
|
|
||||||
test('forwards one exact cryptographic authorization bundle without private material', async () => {
|
test('forwards one exact cryptographic authorization bundle without private material', async () => {
|
||||||
const requests: unknown[] = [];
|
const requests: unknown[] = [];
|
||||||
const source = new StrictDomainRuntimeHandoffSource({
|
const source = new StrictDomainRuntimeHandoffSource(handoffEndpoint(), {
|
||||||
async request(input) {
|
async request(_endpoint, input) {
|
||||||
requests.push(input);
|
requests.push(input);
|
||||||
return response(input.requestId);
|
return response(input.requestId);
|
||||||
},
|
},
|
||||||
|
|
@ -112,7 +144,7 @@ test('forwards one exact cryptographic authorization bundle without private mate
|
||||||
|
|
||||||
assert.equal(requests.length, 1);
|
assert.equal(requests.length, 1);
|
||||||
assert.deepEqual(Object.keys(requests[0] as object).sort(), [
|
assert.deepEqual(Object.keys(requests[0] as object).sort(), [
|
||||||
'accountId', 'challenge', 'domainId', 'issuedAt', 'nodeId', 'nodeType',
|
'accountId', 'challenge', 'domainId', 'endpointId', 'issuedAt', 'nodeId', 'nodeType',
|
||||||
'registration', 'requestId', 'response', 'schema',
|
'registration', 'requestId', 'response', 'schema',
|
||||||
]);
|
]);
|
||||||
assert.equal(JSON.stringify(requests[0]).includes('PRIVATE KEY'), false);
|
assert.equal(JSON.stringify(requests[0]).includes('PRIVATE KEY'), false);
|
||||||
|
|
@ -134,7 +166,7 @@ test('binds the response to the generated request and rejects malformed envelope
|
||||||
response('HANDOFF-REQ-001', { handoff: null, signer_lookup: null, status: 'NOT_AUTHORIZED' }),
|
response('HANDOFF-REQ-001', { handoff: null, signer_lookup: null, status: 'NOT_AUTHORIZED' }),
|
||||||
];
|
];
|
||||||
for (const sample of samples) {
|
for (const sample of samples) {
|
||||||
const source = new StrictDomainRuntimeHandoffSource({ async request() { return sample; } }, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
const source = new StrictDomainRuntimeHandoffSource(handoffEndpoint(), { async request() { return sample; } }, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
||||||
assert.equal(await source.read(verifiedPossession()), null);
|
assert.equal(await source.read(verifiedPossession()), null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -142,22 +174,139 @@ test('binds the response to the generated request and rejects malformed envelope
|
||||||
test('rejects copied, expired and replayed possession authorizations before transport', async () => {
|
test('rejects copied, expired and replayed possession authorizations before transport', async () => {
|
||||||
let requests = 0;
|
let requests = 0;
|
||||||
const possession = verifiedPossession();
|
const possession = verifiedPossession();
|
||||||
const source = new StrictDomainRuntimeHandoffSource({
|
const source = new StrictDomainRuntimeHandoffSource(handoffEndpoint(), {
|
||||||
async request(input) { requests += 1; return response(input.requestId); },
|
async request(_endpoint, input) { requests += 1; return response(input.requestId); },
|
||||||
}, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
}, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
||||||
assert.ok(await source.read(possession));
|
assert.ok(await source.read(possession));
|
||||||
assert.equal(await source.read(possession), null);
|
assert.equal(await source.read(possession), null);
|
||||||
assert.equal(await source.read({ ...verifiedPossession() }), null);
|
assert.equal(await source.read({ ...verifiedPossession() }), null);
|
||||||
const expired = new StrictDomainRuntimeHandoffSource({
|
const expired = new StrictDomainRuntimeHandoffSource(handoffEndpoint(), {
|
||||||
async request(input) { requests += 1; return response(input.requestId); },
|
async request(_endpoint, input) { requests += 1; return response(input.requestId); },
|
||||||
}, () => NOW + 60_000, () => 'HANDOFF-REQ-002');
|
}, () => NOW + 60_000, () => 'HANDOFF-REQ-002');
|
||||||
assert.equal(await expired.read(verifiedPossession()), null);
|
assert.equal(await expired.read(verifiedPossession()), null);
|
||||||
assert.equal(requests, 1);
|
assert.equal(requests, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fails closed on transport errors without exposing remote details', async () => {
|
test('fails closed on transport errors without exposing remote details', async () => {
|
||||||
const source = new StrictDomainRuntimeHandoffSource({
|
const source = new StrictDomainRuntimeHandoffSource(handoffEndpoint(), {
|
||||||
async request() { throw new Error('secret remote detail'); },
|
async request() { throw new Error('secret remote detail'); },
|
||||||
}, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
}, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
||||||
assert.equal(await source.read(verifiedPossession()), null);
|
assert.equal(await source.read(verifiedPossession()), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handoffAnchor(includeMap = true) {
|
||||||
|
return {
|
||||||
|
schema: 'guanghu.public-navigation-anchor/v1',
|
||||||
|
anchor_id: 'GLW-PUBLIC-NAV-ANCHOR-001',
|
||||||
|
state: 'CURRENT_CANONICAL',
|
||||||
|
repository_id: 'REPO-012',
|
||||||
|
branch: 'main',
|
||||||
|
public_entry: 'https://guanghulab.com/api/ai/v1/anchor',
|
||||||
|
code_entry: 'https://guanghulab.com/code/bingshuo/guanghu-ice-heart',
|
||||||
|
maps: includeMap ? {
|
||||||
|
domain_runtime_handoff_endpoints: {
|
||||||
|
path: 'routing/domain-runtime-handoff-endpoints.json',
|
||||||
|
id: 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
} : {},
|
||||||
|
navigation_source: {
|
||||||
|
anchor_id: 'GLW-PUBLIC-NAV-ANCHOR-001',
|
||||||
|
source_commit: 'b'.repeat(40),
|
||||||
|
source_mode: 'REPO-012_MAIN_GIT_SNAPSHOT',
|
||||||
|
source_degraded: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handoffRegistry() {
|
||||||
|
return {
|
||||||
|
endpoints: [{
|
||||||
|
domainIds: [DOMAIN_ID], endpointId: 'GH-DOMAIN-HANDOFF-001', nodeTypes: [NODE_TYPE],
|
||||||
|
status: 'ACTIVE', url: 'https://guanghulab.com/api/ai/v1/domain-runtime/handoff',
|
||||||
|
}],
|
||||||
|
registryId: 'GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001',
|
||||||
|
schema: 'gh-aios.domain-runtime-handoff-endpoints/v1',
|
||||||
|
state: 'CURRENT', version: '1.0.0',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function anchoredLoader(includeMap = true) {
|
||||||
|
return new DomainRuntimeHandoffSnapshotLoader({
|
||||||
|
async fetchJson(url) {
|
||||||
|
if (url.endsWith('/api/ai/v1/anchor')) return handoffAnchor(includeMap);
|
||||||
|
if (url.includes('/raw/commit/')) return handoffRegistry();
|
||||||
|
throw new Error('unexpected_url');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test('anchored source resolves the exact endpoint before consuming possession', async () => {
|
||||||
|
const requests: Array<{ endpoint: string; endpointId: string }> = [];
|
||||||
|
const source = new AnchoredDomainRuntimeHandoffSource(anchoredLoader(), {
|
||||||
|
async request(endpoint, input) {
|
||||||
|
requests.push({ endpoint: endpoint.url, endpointId: input.endpointId });
|
||||||
|
return response(input.requestId);
|
||||||
|
},
|
||||||
|
}, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
||||||
|
assert.ok(await source.read(verifiedPossession()));
|
||||||
|
assert.deepEqual(requests, [{
|
||||||
|
endpoint: 'https://guanghulab.com/api/ai/v1/domain-runtime/handoff',
|
||||||
|
endpointId: 'GH-DOMAIN-HANDOFF-001',
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('missing anchored registry fails before network and does not consume possession', async () => {
|
||||||
|
let requests = 0;
|
||||||
|
const possession = verifiedPossession();
|
||||||
|
const unavailable = new AnchoredDomainRuntimeHandoffSource(anchoredLoader(false), {
|
||||||
|
async request() { requests += 1; return null; },
|
||||||
|
}, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
||||||
|
assert.equal(await unavailable.read(possession), null);
|
||||||
|
assert.equal(requests, 0);
|
||||||
|
const current = new AnchoredDomainRuntimeHandoffSource(anchoredLoader(), {
|
||||||
|
async request(_endpoint, input) { requests += 1; return response(input.requestId); },
|
||||||
|
}, () => NOW + 2, () => 'HANDOFF-REQ-002');
|
||||||
|
assert.ok(await current.read(possession));
|
||||||
|
assert.equal(requests, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('HTTP transport posts only to the registered endpoint without credentials', async () => {
|
||||||
|
const endpoint = handoffEndpoint();
|
||||||
|
let observedUrl = '';
|
||||||
|
let observedInit: RequestInit | undefined;
|
||||||
|
const transport = new HttpDomainRuntimeHandoffTransport(async (url, init) => {
|
||||||
|
observedUrl = String(url);
|
||||||
|
observedInit = init;
|
||||||
|
return new Response(JSON.stringify(response('HANDOFF-REQ-001')), {
|
||||||
|
headers: { 'Content-Type': 'application/json' }, status: 200,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const source = new StrictDomainRuntimeHandoffSource(endpoint, transport, () => NOW + 2, () => 'HANDOFF-REQ-001');
|
||||||
|
assert.ok(await source.read(verifiedPossession()));
|
||||||
|
assert.equal(observedUrl, endpoint.url);
|
||||||
|
assert.equal(observedInit?.method, 'POST');
|
||||||
|
assert.equal(observedInit?.credentials, 'omit');
|
||||||
|
assert.equal(observedInit?.redirect, 'manual');
|
||||||
|
assert.equal(observedInit?.cache, 'no-store');
|
||||||
|
assert.equal(observedInit?.referrerPolicy, 'no-referrer');
|
||||||
|
assert.equal(JSON.stringify(observedInit?.headers).includes('Authorization'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('HTTP transport rejects redirects, non-JSON and oversized responses', async () => {
|
||||||
|
const endpoint = handoffEndpoint();
|
||||||
|
const request = Object.freeze({
|
||||||
|
accountId: 'bingshuo', challenge: {} as never, domainId: DOMAIN_ID,
|
||||||
|
endpointId: endpoint.endpointId, issuedAt: NOW, nodeId: NODE_ID, nodeType: NODE_TYPE,
|
||||||
|
registration: {} as never, requestId: 'HANDOFF-REQ-001', response: {} as never,
|
||||||
|
schema: 'gh-aios.domain-runtime-handoff-request/v1' as const,
|
||||||
|
});
|
||||||
|
for (const reply of [
|
||||||
|
new Response('', { status: 302, headers: { Location: 'https://example.com/' } }),
|
||||||
|
new Response('plain', { status: 200, headers: { 'Content-Type': 'text/plain' } }),
|
||||||
|
new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json', 'Content-Length': '262145' } }),
|
||||||
|
]) {
|
||||||
|
const transport = new HttpDomainRuntimeHandoffTransport(async () => reply);
|
||||||
|
await assert.rejects(() => transport.request(endpoint, request));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,27 @@ import {
|
||||||
} from './node-possession-proof.js';
|
} from './node-possession-proof.js';
|
||||||
import type { VerifiedNodeRegistration } from './node-registration.js';
|
import type { VerifiedNodeRegistration } from './node-registration.js';
|
||||||
import { randomUUID } from 'node:crypto';
|
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 IDENTIFIER_PATTERN = /^[A-Z0-9][A-Z0-9._:-]{1,159}$/;
|
||||||
const REQUEST_SCHEMA = 'gh-aios.domain-runtime-handoff-request/v1' as const;
|
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 RESPONSE_SCHEMA = 'gh-aios.domain-runtime-handoff-response/v1' as const;
|
||||||
const consumedPossessions = new WeakSet<object>();
|
const consumedPossessions = new WeakSet<object>();
|
||||||
|
const MAX_RESPONSE_BYTES = 256 * 1024;
|
||||||
|
|
||||||
export interface DomainRuntimeHandoffRequest {
|
export interface DomainRuntimeHandoffRequest {
|
||||||
accountId: string;
|
accountId: string;
|
||||||
challenge: Readonly<NodePossessionChallenge>;
|
challenge: Readonly<NodePossessionChallenge>;
|
||||||
domainId: string;
|
domainId: string;
|
||||||
|
endpointId: string;
|
||||||
issuedAt: number;
|
issuedAt: number;
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
nodeType: VerifiedNodeRegistration['nodeType'];
|
nodeType: VerifiedNodeRegistration['nodeType'];
|
||||||
|
|
@ -30,7 +41,10 @@ export interface DomainRuntimeHandoffRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DomainRuntimeHandoffTransport {
|
export interface DomainRuntimeHandoffTransport {
|
||||||
request(input: Readonly<DomainRuntimeHandoffRequest>): Promise<unknown>;
|
request(
|
||||||
|
endpoint: Readonly<DomainRuntimeHandoffEndpoint>,
|
||||||
|
input: Readonly<DomainRuntimeHandoffRequest>,
|
||||||
|
): Promise<unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
|
@ -76,6 +90,7 @@ function parseResponse(
|
||||||
|
|
||||||
export class StrictDomainRuntimeHandoffSource implements DomainRuntimeHandoffSource {
|
export class StrictDomainRuntimeHandoffSource implements DomainRuntimeHandoffSource {
|
||||||
constructor(
|
constructor(
|
||||||
|
private readonly endpoint: DomainRuntimeHandoffEndpoint,
|
||||||
private readonly transport: DomainRuntimeHandoffTransport,
|
private readonly transport: DomainRuntimeHandoffTransport,
|
||||||
private readonly clock: () => number = Date.now,
|
private readonly clock: () => number = Date.now,
|
||||||
private readonly requestId: () => string = () => `HANDOFF-REQ-${randomUUID().toUpperCase()}`,
|
private readonly requestId: () => string = () => `HANDOFF-REQ-${randomUUID().toUpperCase()}`,
|
||||||
|
|
@ -85,6 +100,10 @@ export class StrictDomainRuntimeHandoffSource implements DomainRuntimeHandoffSou
|
||||||
try {
|
try {
|
||||||
const verified = assertVerifiedNodePossession(possession);
|
const verified = assertVerifiedNodePossession(possession);
|
||||||
if (consumedPossessions.has(verified)) return null;
|
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 issuedAt = this.clock();
|
||||||
const requestId = this.requestId();
|
const requestId = this.requestId();
|
||||||
if (!Number.isSafeInteger(issuedAt)
|
if (!Number.isSafeInteger(issuedAt)
|
||||||
|
|
@ -96,6 +115,7 @@ export class StrictDomainRuntimeHandoffSource implements DomainRuntimeHandoffSou
|
||||||
accountId: verified.registration.accountId,
|
accountId: verified.registration.accountId,
|
||||||
challenge: verified.challenge,
|
challenge: verified.challenge,
|
||||||
domainId: verified.registration.domainId,
|
domainId: verified.registration.domainId,
|
||||||
|
endpointId: endpoint.endpointId,
|
||||||
issuedAt,
|
issuedAt,
|
||||||
nodeId: verified.registration.nodeId,
|
nodeId: verified.registration.nodeId,
|
||||||
nodeType: verified.registration.nodeType,
|
nodeType: verified.registration.nodeType,
|
||||||
|
|
@ -105,9 +125,82 @@ export class StrictDomainRuntimeHandoffSource implements DomainRuntimeHandoffSou
|
||||||
schema: REQUEST_SCHEMA,
|
schema: REQUEST_SCHEMA,
|
||||||
});
|
});
|
||||||
consumedPossessions.add(verified);
|
consumedPossessions.add(verified);
|
||||||
return parseResponse(await this.transport.request(request), request);
|
return parseResponse(await this.transport.request(endpoint, request), request);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
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'); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@ import { DomainAccessOrchestrator } from '../../guanghu-knowledge-base/server/do
|
||||||
import { TrustedSignerSnapshotLoader } from '../../guanghu-knowledge-base/server/trusted-signer-snapshot.js';
|
import { TrustedSignerSnapshotLoader } from '../../guanghu-knowledge-base/server/trusted-signer-snapshot.js';
|
||||||
import { NodeRegistrationSnapshotLoader } from '../../guanghu-knowledge-base/server/node-registration-snapshot.js';
|
import { NodeRegistrationSnapshotLoader } from '../../guanghu-knowledge-base/server/node-registration-snapshot.js';
|
||||||
import { HttpNodeRegistrationClaimSource } from '../../guanghu-knowledge-base/server/node-registration-client.js';
|
import { HttpNodeRegistrationClaimSource } from '../../guanghu-knowledge-base/server/node-registration-client.js';
|
||||||
|
import { DomainRuntimeHandoffSnapshotLoader } from '../../guanghu-knowledge-base/server/domain-runtime-handoff-snapshot.js';
|
||||||
|
import {
|
||||||
|
AnchoredDomainRuntimeHandoffSource,
|
||||||
|
HttpDomainRuntimeHandoffTransport,
|
||||||
|
} from '../../guanghu-knowledge-base/server/domain-runtime-handoff-source.js';
|
||||||
|
|
||||||
// ─── 配置 ───
|
// ─── 配置 ───
|
||||||
|
|
||||||
|
|
@ -339,6 +344,28 @@ const nodeRegistrationSnapshots = new NodeRegistrationSnapshotLoader({
|
||||||
|
|
||||||
const nodeRegistrationClaims = new HttpNodeRegistrationClaimSource();
|
const nodeRegistrationClaims = new HttpNodeRegistrationClaimSource();
|
||||||
|
|
||||||
|
const domainRuntimeHandoffSnapshots = new DomainRuntimeHandoffSnapshotLoader({
|
||||||
|
async fetchJson(url: string): Promise<unknown> {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
cache: 'no-store',
|
||||||
|
credentials: 'omit',
|
||||||
|
headers: { Accept: 'application/json' },
|
||||||
|
redirect: 'manual',
|
||||||
|
referrerPolicy: 'no-referrer',
|
||||||
|
signal: AbortSignal.timeout(5000),
|
||||||
|
});
|
||||||
|
if (!response.ok || response.type === 'opaqueredirect') {
|
||||||
|
throw new Error(`domain_runtime_handoff_source_http_${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const domainRuntimeHandoffs = new AnchoredDomainRuntimeHandoffSource(
|
||||||
|
domainRuntimeHandoffSnapshots,
|
||||||
|
new HttpDomainRuntimeHandoffTransport(),
|
||||||
|
);
|
||||||
|
|
||||||
const domainAccessOrchestrator = new DomainAccessOrchestrator(
|
const domainAccessOrchestrator = new DomainAccessOrchestrator(
|
||||||
trustedSignerSnapshots,
|
trustedSignerSnapshots,
|
||||||
nodeRegistrationSnapshots,
|
nodeRegistrationSnapshots,
|
||||||
|
|
@ -366,12 +393,7 @@ const domainAccessOrchestrator = new DomainAccessOrchestrator(
|
||||||
// proof keeps the domain gate closed even if a future signed claim appears.
|
// proof keeps the domain gate closed even if a future signed claim appears.
|
||||||
async prove() { return null; },
|
async prove() { return null; },
|
||||||
},
|
},
|
||||||
{
|
domainRuntimeHandoffs,
|
||||||
async read() {
|
|
||||||
// 当前灯塔尚未发布签名运行体交接端点。保持关闭,不从账号身份推导运行体权限。
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
function startServer(): Promise<void> {
|
function startServer(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -99,3 +99,22 @@ REPO-012 信任快照独立验证 handoff 中的域清单签名、短期会话
|
||||||
本阶段闭合的是“证明能够被带到交接边界”的客户端证据结构,不是生产 HTTP 端点。当前公共
|
本阶段闭合的是“证明能够被带到交接边界”的客户端证据结构,不是生产 HTTP 端点。当前公共
|
||||||
锚点没有域交接端点注册表,桌面运行源仍返回空结果,因此生产会话签发、域运行体连接与在线
|
锚点没有域交接端点注册表,桌面运行源仍返回空结果,因此生产会话签发、域运行体连接与在线
|
||||||
回执继续为 `NOT_EXISTS_0`。
|
回执继续为 `NOT_EXISTS_0`。
|
||||||
|
|
||||||
|
## 域运行体交接端点锚定与严格传输 · 阶段 005
|
||||||
|
|
||||||
|
本阶段先固定以下验收标准,再进入实现:
|
||||||
|
|
||||||
|
1. 域交接 URL 只能来自 REPO-012 当前公共锚点同一提交声明的
|
||||||
|
`GH-AIOS-DOMAIN-RUNTIME-HANDOFF-ENDPOINTS-001`,账号响应、调用方参数、本地配置和复制对象
|
||||||
|
都不能指定或替换 URL。
|
||||||
|
2. 注册表只允许无凭据的 `https://guanghulab.com/api/ai/v1/...` 端点;禁止端口、用户名、密码、
|
||||||
|
查询串、片段和重定向。同一域与节点类型最多一个活跃端点。
|
||||||
|
3. 只有来源状态为 `CURRENT` 的已登记快照和已登记端点才能发起请求;注册表缺失、来源降级、
|
||||||
|
范围不匹配、端点撤销或调用方伪造对象时必须在网络请求前失败关闭。
|
||||||
|
4. 交接传输固定为无凭据 `POST application/json`,5 秒超时、`redirect=manual`、
|
||||||
|
`credentials=omit`、`cache=no-store`、`referrerPolicy=no-referrer`;响应必须是 JSON 且不超过
|
||||||
|
256 KiB。HTTP 错误、重定向、非 JSON、超限和响应绑定错误均返回空结果。
|
||||||
|
5. 请求包必须增加并绑定 `endpointId`,已验证节点证明只有在解析到当前活跃端点后才被消耗,
|
||||||
|
且一次证明仍最多发起一次网络请求。
|
||||||
|
6. 当前公共锚点尚未登记域交接端点映射,生产桌面必须不发送交接请求并继续返回
|
||||||
|
`NOT_EXISTS_0`;代码完成不得冒充生产端点、会话签发、域运行体连接或在线健康已经存在。
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
- [`HOLOLAKE-INTENT-REASONING-MAP-20260809.md`](HOLOLAKE-INTENT-REASONING-MAP-20260809.md):当前产品意图、纠正、思维推理与因果链。
|
- [`HOLOLAKE-INTENT-REASONING-MAP-20260809.md`](HOLOLAKE-INTENT-REASONING-MAP-20260809.md):当前产品意图、纠正、思维推理与因果链。
|
||||||
- [`HOLOLAKE-SYSTEM-ARCHITECTURE-20260809.md`](HOLOLAKE-SYSTEM-ARCHITECTURE-20260809.md):`HLP-CURRENT-ARCH-001` 当前完整系统蓝图,覆盖 GH-AIOS 灯塔、五域独立入口、光湖本源域、节点登录与光湖桥。
|
- [`HOLOLAKE-SYSTEM-ARCHITECTURE-20260809.md`](HOLOLAKE-SYSTEM-ARCHITECTURE-20260809.md):`HLP-CURRENT-ARCH-001` 当前完整系统蓝图,覆盖 GH-AIOS 灯塔、五域独立入口、光湖本源域、节点登录与光湖桥。
|
||||||
- [`HOLOLAKE-DESKTOP-0.8.0-CURRENT-CAPABILITY-GAP-20260809.md`](HOLOLAKE-DESKTOP-0.8.0-CURRENT-CAPABILITY-GAP-20260809.md):桌面 0.8.0 已观察能力与最终规划的逐项差距;同时登记线上产品源码仍为 0.4.6 的分叉事实。
|
- [`HOLOLAKE-DESKTOP-0.8.0-CURRENT-CAPABILITY-GAP-20260809.md`](HOLOLAKE-DESKTOP-0.8.0-CURRENT-CAPABILITY-GAP-20260809.md):桌面 0.8.0 已观察能力与最终规划的逐项差距;同时登记线上产品源码仍为 0.4.6 的分叉事实。
|
||||||
- [`HOLOLAKE-NODE-REGISTRATION-CONTRACT-20260810.md`](HOLOLAKE-NODE-REGISTRATION-CONTRACT-20260810.md):账号、签名节点登记与当前节点私钥持有证明分离后的端点注册表、无凭据声明读取、60 秒一次性 Ed25519 挑战、不可伪造交接证据包、失败关闭和当前生产缺口验收基线。
|
- [`HOLOLAKE-NODE-REGISTRATION-CONTRACT-20260810.md`](HOLOLAKE-NODE-REGISTRATION-CONTRACT-20260810.md):账号、签名节点登记与当前节点私钥持有证明分离后的端点注册表、无凭据声明读取、60 秒一次性 Ed25519 挑战、不可伪造交接证据包,以及只能由 REPO-012 当前公共锚点登记 URL 的严格域交接传输;当前锚点未登记映射,生产继续失败关闭。
|
||||||
- [`HOLOLAKE-LANGUAGE-PERSONA-OS.md`](HOLOLAKE-LANGUAGE-PERSONA-OS.md):HoloLake 作为 AI 语言人格驱动操作系统的当前正式产品定位、操作系统映射、原生应用边界与实现缺口。
|
- [`HOLOLAKE-LANGUAGE-PERSONA-OS.md`](HOLOLAKE-LANGUAGE-PERSONA-OS.md):HoloLake 作为 AI 语言人格驱动操作系统的当前正式产品定位、操作系统映射、原生应用边界与实现缺口。
|
||||||
- [`HOLOLAKE-INTENT-REASONING-MAP-20260807.md`](HOLOLAKE-INTENT-REASONING-MAP-20260807.md):2026-08-07 历史意图层,继续保存形成过程;不再作为当前默认入口。
|
- [`HOLOLAKE-INTENT-REASONING-MAP-20260807.md`](HOLOLAKE-INTENT-REASONING-MAP-20260807.md):2026-08-07 历史意图层,继续保存形成过程;不再作为当前默认入口。
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue