feat(provider-contract): compile governed L2 execution plans

This commit is contained in:
Codex 2026-07-23 19:27:01 +03:00
parent 6f9da1b677
commit a5a5644dbf
9 changed files with 916 additions and 1 deletions

View File

@ -48,6 +48,25 @@ read-capability передаёт все entities, которые provider воз
boundary запрещены; сортировка и видимость принадлежат Data Product consumer и boundary запрещены; сортировка и видимость принадлежат Data Product consumer и
Foundry. Foundry.
`compileL2ExecutionPlan` превращает package + connection instance в
детерминированный `nodedc.l2-execution-plan/v1`: разрешает generic шаги
collection/request/extract/mapping/publish, прикладывает полные декларативные
request/mapping contracts и фиксирует SHA-256 digests package, profile,
template, mapping, field policy, Data Product и telemetry registry. В
компиляторе нет веток по provider ID. Provider-specific URL, source paths и
параметры являются данными immutable package. После материализации Engine
`attestL2ExecutionPlanMaterialization` связывает exact plan digest с exact
graph revision/digest и точным списком materialized steps.
Динамическая телеметрия проходит отдельный
`nodedc.telemetry-field-registry/v1`. Реестр хранит только имена source
parameters, типы, статус классификации, sensitivity и разрешённые surfaces —
никогда значения. Только `approved + operational` entries могут войти в
Data Product/analytics/Foundry projection. `observed`, PII/identifier,
command и restricted entries остаются audit-only; wildcard source key
запрещён. Поэтому появление нового параметра у provider не меняет ядро и не
открывает его автоматически потребителям.
Текущий production-shaped package — `providers/gelios/v5`. Он сохраняет два Текущий production-shaped package — `providers/gelios/v5`. Он сохраняет два
официальных Gelios REST safe-read: `GET /api/v1/users/me/monitoring-config` и официальных Gelios REST safe-read: `GET /api/v1/users/me/monitoring-config` и
`GET /api/v1/units?incltrip=true`, а также точный immutable output `GET /api/v1/units?incltrip=true`, а также точный immutable output

View File

@ -9,6 +9,6 @@
"./providers/*": "./providers/*/index.mjs" "./providers/*": "./providers/*/index.mjs"
}, },
"scripts": { "scripts": {
"check": "node test/contract.test.mjs && node test/data-product.test.mjs && node test/provider-package.test.mjs && node test/provider-capability-catalog.test.mjs && node test/zone-source.test.mjs && node test/engine-private-extension.test.mjs" "check": "node test/contract.test.mjs && node test/data-product.test.mjs && node test/provider-package.test.mjs && node test/provider-capability-catalog.test.mjs && node test/telemetry-field-registry.test.mjs && node test/l2-execution-plan.test.mjs && node test/zone-source.test.mjs && node test/engine-private-extension.test.mjs"
} }
} }

View File

@ -6,3 +6,4 @@ export {
GELIOS_UNIT_PROFILE_ONTOLOGY_REVISION, GELIOS_UNIT_PROFILE_ONTOLOGY_REVISION,
geliosProviderPackageV8, geliosProviderPackageV8,
} from "./package.mjs"; } from "./package.mjs";
export { geliosTelemetryFieldRegistryV1 } from "./telemetry-field-registry.mjs";

View File

@ -0,0 +1,75 @@
import {
TELEMETRY_FIELD_REGISTRY_SCHEMA_VERSION,
} from "../../../src/telemetry-field-registry.mjs";
import {
GELIOS_PROVIDER_PACKAGE_ID,
GELIOS_PROVIDER_PACKAGE_VERSION,
} from "./package.mjs";
import {
GELIOS_POSITIONS_DATA_PRODUCT_ID,
} from "../v7/package.mjs";
const value = {
schemaVersion: TELEMETRY_FIELD_REGISTRY_SCHEMA_VERSION,
id: "gelios.units.current.telemetry.v1",
version: "1.0.0",
providerPackage: {
id: GELIOS_PROVIDER_PACKAGE_ID,
version: GELIOS_PROVIDER_PACKAGE_VERSION,
},
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
sourceNamespace: "gelios.units.configured_sensor_parameter",
entries: [
{
id: "gelios.sensor-parameter.in0",
sourceKey: "in0",
status: "approved",
sensitivity: "operational",
valueType: "number",
semanticReading: {
id: "sensor.param.in0",
labelSource: "provider_configured",
unitSource: "provider_configured",
},
allowedSurfaces: ["audit", "data_product", "analytics", "foundry"],
},
{
id: "gelios.sensor-parameter.in1",
sourceKey: "in1",
status: "approved",
sensitivity: "operational",
valueType: "number",
semanticReading: {
id: "sensor.param.in1",
labelSource: "provider_configured",
unitSource: "provider_configured",
},
allowedSurfaces: ["audit", "data_product", "analytics", "foundry"],
},
{
id: "gelios.sensor-parameter.in-underscore-0",
sourceKey: "in_0",
status: "observed",
sensitivity: "unclassified",
valueType: "unknown",
allowedSurfaces: ["audit"],
},
{
id: "gelios.sensor-parameter.in-underscore-1",
sourceKey: "in_1",
status: "observed",
sensitivity: "unclassified",
valueType: "unknown",
allowedSurfaces: ["audit"],
},
],
};
export const geliosTelemetryFieldRegistryV1 = deepFreeze(value);
function deepFreeze(input) {
if (!input || typeof input !== "object" || Object.isFrozen(input)) return input;
Object.freeze(input);
for (const child of Object.values(input)) deepFreeze(child);
return input;
}

View File

@ -14,6 +14,21 @@ export {
TELEMETRY_READINGS_MAX_ITEMS, TELEMETRY_READINGS_MAX_ITEMS,
isBoundedTelemetryReadings, isBoundedTelemetryReadings,
} from "./telemetry-readings.mjs"; } from "./telemetry-readings.mjs";
export {
TELEMETRY_FIELD_PROJECTION_SCHEMA_VERSION,
TELEMETRY_FIELD_REGISTRY_SCHEMA_VERSION,
compileTelemetryFieldProjection,
digestTelemetryFieldRegistry,
validateTelemetryFieldRegistry,
} from "./telemetry-field-registry.mjs";
export {
L2_EXECUTION_PLAN_COMPILER_VERSION,
L2_EXECUTION_PLAN_SCHEMA_VERSION,
L2_MATERIALIZATION_RECEIPT_SCHEMA_VERSION,
attestL2ExecutionPlanMaterialization,
compileL2ExecutionPlan,
validateL2ExecutionPlan,
} from "./l2-execution-plan.mjs";
export { export {
ZONE_SOURCE_ADAPTERS, ZONE_SOURCE_ADAPTERS,
ZONE_SOURCE_GENERATION_SCHEMA_VERSION, ZONE_SOURCE_GENERATION_SCHEMA_VERSION,

View File

@ -0,0 +1,333 @@
import { createHash } from "node:crypto";
import { validateProviderPackage } from "./provider-package.mjs";
import {
compileTelemetryFieldProjection,
validateTelemetryFieldRegistry,
} from "./telemetry-field-registry.mjs";
export const L2_EXECUTION_PLAN_SCHEMA_VERSION = "nodedc.l2-execution-plan/v1";
export const L2_MATERIALIZATION_RECEIPT_SCHEMA_VERSION = "nodedc.l2-materialization-receipt/v1";
export const L2_EXECUTION_PLAN_COMPILER_VERSION = "1.0.0";
const HASH = /^(?:sha256:)?[a-f0-9]{64}$/;
const IDENTIFIER = /^[a-z][a-z0-9._:-]{2,127}$/;
const CREDENTIAL_REF = /^ndc-credref:[A-Za-z0-9._:-]{8,255}$/;
const COMPILE_OPTION_KEYS = new Set(["telemetryFieldRegistry"]);
const RECEIPT_OPTION_KEYS = new Set(["graphRevision", "graphDigest", "materializedStepIds"]);
/**
* Resolve one immutable provider package plus one connection instance into a
* deterministic, provider-neutral execution plan. Provider-specific knowledge
* stays in package/registry data; compiler branching is only by generic step
* kind.
*/
export function compileL2ExecutionPlan(providerPackage, connectionInstance, options = {}) {
const packageValidation = validateProviderPackage(providerPackage);
if (!packageValidation.ok) {
throw new Error(`provider_package_invalid:${packageValidation.errors.join(",")}`);
}
validateCompileOptions(options);
validateConnectionInstance(providerPackage, connectionInstance);
const profile = requireById(
providerPackage.collectionProfiles,
connectionInstance.collectionProfileId,
"collection_profile",
);
const template = requireById(providerPackage.l2Templates, profile.l2TemplateId, "l2_template");
if (template.id !== connectionInstance.l2TemplateId) {
throw new Error("l2_execution_plan_connection_template_mismatch");
}
const mapping = requireById(providerPackage.mappingContracts, profile.mappingContractId, "mapping_contract");
const fieldPolicy = requireById(providerPackage.fieldPolicies, profile.fieldPolicyId, "field_policy");
const dataProduct = requireById(providerPackage.dataProducts, profile.dataProductId, "data_product");
const requiresTelemetryRegistry = Object.values(dataProduct.fieldContracts || {})
.some((contract) => contract?.type === "telemetry_readings");
let telemetryProjection;
if (options.telemetryFieldRegistry !== undefined) {
const registryValidation = validateTelemetryFieldRegistry(options.telemetryFieldRegistry);
if (!registryValidation.ok) {
throw new Error(`telemetry_field_registry_invalid:${registryValidation.errors.join(",")}`);
}
telemetryProjection = compileTelemetryFieldProjection(options.telemetryFieldRegistry, {
providerPackageId: providerPackage.id,
providerPackageVersion: providerPackage.version,
dataProductId: dataProduct.id,
surface: "data_product",
});
} else if (requiresTelemetryRegistry) {
throw new Error("l2_execution_plan_telemetry_registry_required");
}
if (requiresTelemetryRegistry && telemetryProjection.entries.length === 0) {
throw new Error("l2_execution_plan_telemetry_projection_empty");
}
if (!requiresTelemetryRegistry && telemetryProjection !== undefined) {
throw new Error("l2_execution_plan_telemetry_registry_not_applicable");
}
const artifacts = {
packageDigest: canonicalDigest(providerPackage),
collectionProfileDigest: canonicalDigest(profile),
l2TemplateDigest: canonicalDigest(template),
mappingContractDigest: canonicalDigest(mapping),
fieldPolicyDigest: canonicalDigest(fieldPolicy),
dataProductDigest: canonicalDigest(dataProduct),
...(telemetryProjection ? { telemetryRegistryDigest: telemetryProjection.registry.digest } : {}),
};
const steps = template.steps.map((step) => compileStep({
step,
providerPackage,
profile,
mapping,
fieldPolicy,
dataProduct,
telemetryProjection,
artifacts,
}));
const planWithoutDigest = {
schemaVersion: L2_EXECUTION_PLAN_SCHEMA_VERSION,
compilerVersion: L2_EXECUTION_PLAN_COMPILER_VERSION,
package: {
id: providerPackage.id,
providerId: providerPackage.providerId,
version: providerPackage.version,
},
connection: {
tenantId: connectionInstance.tenantId,
connectionId: connectionInstance.connectionId,
collectionProfileId: profile.id,
l2TemplateId: template.id,
},
bindings: {
provider: structuredClone(connectionInstance.credentialRefs.provider),
publisher: structuredClone(connectionInstance.systemBindings.publisher),
},
collectionProfile: structuredClone(profile),
artifacts,
steps,
};
return deepFreeze({
...planWithoutDigest,
executionPlanDigest: canonicalDigest(planWithoutDigest),
});
}
export function validateL2ExecutionPlan(value) {
const errors = [];
if (!isPlainObject(value)) return result(["executionPlan_must_be_object"]);
if (value.schemaVersion !== L2_EXECUTION_PLAN_SCHEMA_VERSION) errors.push("executionPlan.schemaVersion_mismatch");
if (value.compilerVersion !== L2_EXECUTION_PLAN_COMPILER_VERSION) errors.push("executionPlan.compilerVersion_mismatch");
if (!Array.isArray(value.steps) || value.steps.length < 5) {
errors.push("executionPlan.steps_invalid");
} else {
const ids = value.steps.map((step) => step?.id);
if (ids.some((id) => typeof id !== "string" || !IDENTIFIER.test(id))) errors.push("executionPlan.steps_id_invalid");
if (new Set(ids).size !== ids.length) errors.push("executionPlan.steps_id_must_be_unique");
}
if (!HASH.test(String(value.executionPlanDigest || ""))) {
errors.push("executionPlan.executionPlanDigest_invalid");
} else {
const descriptor = structuredClone(value);
delete descriptor.executionPlanDigest;
if (normalizeDigest(value.executionPlanDigest) !== canonicalDigest(descriptor)) {
errors.push("executionPlan.executionPlanDigest_mismatch");
}
}
return result(errors);
}
/**
* Bind a successful Engine graph materialization back to the exact compiled
* plan without teaching this package how Engine stores or renders its graph.
*/
export function attestL2ExecutionPlanMaterialization(executionPlan, options) {
const validation = validateL2ExecutionPlan(executionPlan);
if (!validation.ok) {
throw new Error(`l2_execution_plan_invalid:${validation.errors.join(",")}`);
}
if (!isPlainObject(options)) throw new Error("l2_materialization_options_must_be_object");
const errors = [];
rejectUnknownKeys(options, RECEIPT_OPTION_KEYS, "options", errors);
if (typeof options.graphRevision !== "string" || !options.graphRevision.trim() || options.graphRevision.length > 160) {
errors.push("options.graphRevision_invalid");
}
if (!HASH.test(String(options.graphDigest || ""))) errors.push("options.graphDigest_invalid");
const expectedStepIds = executionPlan.steps.map((step) => step.id);
if (!Array.isArray(options.materializedStepIds)
|| options.materializedStepIds.length !== expectedStepIds.length
|| options.materializedStepIds.some((id, index) => id !== expectedStepIds[index])) {
errors.push("options.materializedStepIds_must_exactly_match_execution_plan");
}
if (errors.length) {
throw new Error(`l2_materialization_invalid:${[...new Set(errors)].join(",")}`);
}
const receiptWithoutDigest = {
schemaVersion: L2_MATERIALIZATION_RECEIPT_SCHEMA_VERSION,
compilerVersion: executionPlan.compilerVersion,
executionPlanDigest: executionPlan.executionPlanDigest,
graphRevision: options.graphRevision,
graphDigest: normalizeDigest(options.graphDigest),
materializedStepIds: [...options.materializedStepIds],
};
return deepFreeze({
...receiptWithoutDigest,
receiptDigest: canonicalDigest(receiptWithoutDigest),
});
}
function compileStep(context) {
const { step, providerPackage, profile, mapping, fieldPolicy, dataProduct, telemetryProjection, artifacts } = context;
const base = { id: step.id, kind: step.kind };
if (step.kind === "collection_trigger") {
return {
...base,
config: {
mode: profile.mode,
...(profile.schedule ? { schedule: structuredClone(profile.schedule) } : {}),
},
};
}
if (step.kind === "provider_request") {
const capability = requireById(providerPackage.capabilities, step.capabilityId, "capability");
return {
...base,
config: {
capabilityId: capability.id,
capabilityDigest: canonicalDigest(capability),
authModeId: capability.authModeId,
credentialBinding: "provider",
request: structuredClone(capability.request),
},
};
}
if (step.kind === "extract_items") {
const capability = requireById(providerPackage.capabilities, step.capabilityId, "capability");
return {
...base,
config: {
capabilityId: capability.id,
capabilityDigest: canonicalDigest(capability),
response: structuredClone(capability.request.response),
},
};
}
if (step.kind === "semantic_mapping") {
return {
...base,
config: {
mappingContractId: mapping.id,
mappingContractDigest: artifacts.mappingContractDigest,
fieldPolicyId: fieldPolicy.id,
fieldPolicyDigest: artifacts.fieldPolicyDigest,
dataProductId: dataProduct.id,
dataProductDigest: artifacts.dataProductDigest,
mappingContract: structuredClone(mapping),
...(telemetryProjection ? { telemetryProjection: structuredClone(telemetryProjection) } : {}),
},
};
}
if (step.kind === "data_product_publish") {
return {
...base,
config: {
dataProductId: dataProduct.id,
dataProductDigest: artifacts.dataProductDigest,
nodeType: step.nodeType,
credentialBinding: "publisher",
},
};
}
throw new Error(`l2_execution_plan_step_kind_unsupported:${String(step.kind)}`);
}
function validateConnectionInstance(providerPackage, value) {
if (!isPlainObject(value)) throw new Error("l2_execution_plan_connection_must_be_object");
if (value.schemaVersion !== "nodedc.l2-connection-instance/v1") {
throw new Error("l2_execution_plan_connection_schema_mismatch");
}
if (value.package?.id !== providerPackage.id
|| value.package?.providerId !== providerPackage.providerId
|| value.package?.version !== providerPackage.version) {
throw new Error("l2_execution_plan_connection_package_mismatch");
}
if (typeof value.tenantId !== "string" || !IDENTIFIER.test(value.tenantId)) {
throw new Error("l2_execution_plan_tenant_id_invalid");
}
if (typeof value.connectionId !== "string" || !IDENTIFIER.test(value.connectionId)) {
throw new Error("l2_execution_plan_connection_id_invalid");
}
if (!CREDENTIAL_REF.test(String(value.credentialRefs?.provider?.reference || ""))
|| value.credentialRefs?.provider?.owner !== "ndc_l2_credentials") {
throw new Error("l2_execution_plan_provider_credential_ref_invalid");
}
const publisher = value.systemBindings?.publisher;
if (publisher?.owner !== "ndc_l2_credentials"
|| publisher?.management !== "control_plane_managed"
|| publisher?.desiredState !== "bound"
|| publisher?.status !== "unresolved") {
throw new Error("l2_execution_plan_publisher_binding_invalid");
}
}
function validateCompileOptions(options) {
if (!isPlainObject(options)) throw new Error("l2_execution_plan_options_must_be_object");
const errors = [];
rejectUnknownKeys(options, COMPILE_OPTION_KEYS, "options", errors);
if (errors.length) throw new Error(`l2_execution_plan_options_invalid:${errors.join(",")}`);
}
function requireById(items, id, type) {
const value = Array.isArray(items) ? items.find((item) => item?.id === id) : undefined;
if (!value) throw new Error(`l2_execution_plan_${type}_missing:${String(id)}`);
return value;
}
function canonicalDigest(value) {
return `sha256:${createHash("sha256").update(stableJson(value), "utf8").digest("hex")}`;
}
function normalizeDigest(value) {
const text = String(value);
return text.startsWith("sha256:") ? text : `sha256:${text}`;
}
function stableJson(value) {
return JSON.stringify(stableValue(value));
}
function stableValue(value) {
if (Array.isArray(value)) return value.map(stableValue);
if (!isPlainObject(value)) return value;
return Object.fromEntries(
Object.keys(value)
.filter((key) => value[key] !== undefined)
.sort()
.map((key) => [key, stableValue(value[key])]),
);
}
function rejectUnknownKeys(value, allowed, path, errors) {
for (const key of Object.keys(value)) {
if (!allowed.has(key)) errors.push(`${path}.${key}_not_allowed`);
}
}
function result(errors) {
return { ok: errors.length === 0, errors: [...new Set(errors)] };
}
function isPlainObject(value) {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function deepFreeze(input) {
if (!input || typeof input !== "object" || Object.isFrozen(input)) return input;
Object.freeze(input);
for (const child of Object.values(input)) deepFreeze(child);
return input;
}

View File

@ -0,0 +1,271 @@
import { createHash } from "node:crypto";
export const TELEMETRY_FIELD_REGISTRY_SCHEMA_VERSION = "nodedc.telemetry-field-registry/v1";
export const TELEMETRY_FIELD_PROJECTION_SCHEMA_VERSION = "nodedc.telemetry-field-projection/v1";
const IDENTIFIER = /^[a-z][a-z0-9._:-]{2,127}$/;
const SEMVER = /^\d+\.\d+\.\d+(?:[-+][a-z0-9.-]+)?$/i;
const SOURCE_KEY = /^[A-Za-z_][A-Za-z0-9_.:-]{0,127}$/;
const VALUE_TYPES = new Set(["unknown", "number", "boolean", "string"]);
const STATUSES = new Set(["observed", "classified", "approved", "restricted", "deprecated"]);
const SENSITIVITIES = new Set(["operational", "unclassified", "identifier", "personal", "command", "restricted"]);
const SURFACES = new Set(["audit", "data_product", "analytics", "foundry"]);
const REGISTRY_KEYS = new Set([
"schemaVersion",
"id",
"version",
"providerPackage",
"dataProductId",
"sourceNamespace",
"entries",
]);
const PACKAGE_KEYS = new Set(["id", "version"]);
const ENTRY_KEYS = new Set([
"id",
"sourceKey",
"status",
"sensitivity",
"valueType",
"semanticReading",
"allowedSurfaces",
]);
const READING_KEYS = new Set(["id", "labelSource", "label", "unitSource", "unit"]);
const LABEL_SOURCES = new Set(["registry", "provider_configured"]);
const UNIT_SOURCES = new Set(["none", "registry", "provider_configured"]);
const PROJECTION_OPTION_KEYS = new Set([
"providerPackageId",
"providerPackageVersion",
"dataProductId",
"surface",
]);
/**
* Validate a values-free, provider-scoped registry that classifies dynamic
* telemetry parameters before any Data Product or interface can expose them.
*/
export function validateTelemetryFieldRegistry(value) {
const errors = [];
if (!isPlainObject(value)) return result(["registry_must_be_object"]);
rejectUnknownKeys(value, REGISTRY_KEYS, "registry", errors);
if (value.schemaVersion !== TELEMETRY_FIELD_REGISTRY_SCHEMA_VERSION) {
errors.push("registry.schemaVersion_mismatch");
}
requiredIdentifier(value.id, "registry.id", errors);
requiredSemver(value.version, "registry.version", errors);
requiredIdentifier(value.dataProductId, "registry.dataProductId", errors);
requiredIdentifier(value.sourceNamespace, "registry.sourceNamespace", errors);
if (!isPlainObject(value.providerPackage)) {
errors.push("registry.providerPackage_must_be_object");
} else {
rejectUnknownKeys(value.providerPackage, PACKAGE_KEYS, "registry.providerPackage", errors);
requiredIdentifier(value.providerPackage.id, "registry.providerPackage.id", errors);
requiredSemver(value.providerPackage.version, "registry.providerPackage.version", errors);
}
if (!Array.isArray(value.entries) || value.entries.length === 0 || value.entries.length > 512) {
errors.push("registry.entries_must_be_nonempty_bounded_array");
} else {
const entryIds = new Set();
const sourceKeys = new Set();
const approvedReadingIds = new Set();
value.entries.forEach((entry, index) => {
const path = `registry.entries[${index}]`;
if (!isPlainObject(entry)) {
errors.push(`${path}_must_be_object`);
return;
}
rejectUnknownKeys(entry, ENTRY_KEYS, path, errors);
requiredIdentifier(entry.id, `${path}.id`, errors);
if (entryIds.has(entry.id)) errors.push(`${path}.id_must_be_unique`);
entryIds.add(entry.id);
if (typeof entry.sourceKey !== "string" || !SOURCE_KEY.test(entry.sourceKey)) {
errors.push(`${path}.sourceKey_invalid`);
}
if (sourceKeys.has(entry.sourceKey)) errors.push(`${path}.sourceKey_must_be_unique`);
sourceKeys.add(entry.sourceKey);
if (!STATUSES.has(entry.status)) errors.push(`${path}.status_invalid`);
if (!SENSITIVITIES.has(entry.sensitivity)) errors.push(`${path}.sensitivity_invalid`);
if (!VALUE_TYPES.has(entry.valueType)) errors.push(`${path}.valueType_invalid`);
validateSurfaces(entry.allowedSurfaces, path, errors);
validateSemanticReading(entry.semanticReading, path, errors);
const surfaces = new Set(Array.isArray(entry.allowedSurfaces) ? entry.allowedSurfaces : []);
const externallyExposed = [...surfaces].some((surface) => surface !== "audit");
if (entry.status === "approved") {
if (!isPlainObject(entry.semanticReading)) errors.push(`${path}.approved_requires_semantic_reading`);
if (entry.sensitivity !== "operational") errors.push(`${path}.approved_must_be_operational`);
if (entry.valueType === "unknown") errors.push(`${path}.approved_valueType_must_be_classified`);
if (!surfaces.has("data_product")) errors.push(`${path}.approved_requires_data_product_surface`);
const readingId = entry.semanticReading?.id;
if (typeof readingId === "string") {
if (approvedReadingIds.has(readingId)) errors.push(`${path}.semanticReading.id_must_be_unique`);
approvedReadingIds.add(readingId);
}
} else if (externallyExposed) {
errors.push(`${path}.nonapproved_surface_must_be_audit_only`);
}
if (new Set(["observed", "restricted"]).has(entry.status) && entry.semanticReading !== undefined) {
errors.push(`${path}.${entry.status}_must_not_claim_semantic_reading`);
}
if (entry.status === "observed" && entry.sensitivity !== "unclassified") {
errors.push(`${path}.observed_must_be_unclassified`);
}
if (entry.status === "restricted" && !new Set(["identifier", "personal", "command", "restricted"]).has(entry.sensitivity)) {
errors.push(`${path}.restricted_sensitivity_invalid`);
}
});
}
return result(errors);
}
/**
* Compile an exact, values-free allowlist for one consumer surface. Unknown,
* observed, restricted and deprecated provider parameters are omitted.
*/
export function compileTelemetryFieldProjection(registry, options) {
const validation = validateTelemetryFieldRegistry(registry);
if (!validation.ok) {
throw new Error(`telemetry_field_registry_invalid:${validation.errors.join(",")}`);
}
if (!isPlainObject(options)) throw new Error("telemetry_projection_options_must_be_object");
const optionErrors = [];
rejectUnknownKeys(options, PROJECTION_OPTION_KEYS, "options", optionErrors);
requiredIdentifier(options.providerPackageId, "options.providerPackageId", optionErrors);
requiredSemver(options.providerPackageVersion, "options.providerPackageVersion", optionErrors);
requiredIdentifier(options.dataProductId, "options.dataProductId", optionErrors);
if (!SURFACES.has(options.surface)) optionErrors.push("options.surface_invalid");
if (options.providerPackageId !== registry.providerPackage.id) optionErrors.push("options.providerPackageId_mismatch");
if (options.providerPackageVersion !== registry.providerPackage.version) optionErrors.push("options.providerPackageVersion_mismatch");
if (options.dataProductId !== registry.dataProductId) optionErrors.push("options.dataProductId_mismatch");
if (optionErrors.length) {
throw new Error(`telemetry_projection_invalid:${[...new Set(optionErrors)].join(",")}`);
}
const entries = registry.entries
.filter((entry) => entry.status === "approved" && entry.allowedSurfaces.includes(options.surface))
.map((entry) => ({
registryEntryId: entry.id,
sourceNamespace: registry.sourceNamespace,
sourceKey: entry.sourceKey,
valueType: entry.valueType,
reading: structuredClone(entry.semanticReading),
}))
.sort((left, right) => left.sourceKey.localeCompare(right.sourceKey));
return deepFreeze({
schemaVersion: TELEMETRY_FIELD_PROJECTION_SCHEMA_VERSION,
registry: {
id: registry.id,
version: registry.version,
digest: digestTelemetryFieldRegistry(registry),
},
providerPackage: structuredClone(registry.providerPackage),
dataProductId: registry.dataProductId,
surface: options.surface,
entries,
});
}
export function digestTelemetryFieldRegistry(registry) {
const validation = validateTelemetryFieldRegistry(registry);
if (!validation.ok) {
throw new Error(`telemetry_field_registry_invalid:${validation.errors.join(",")}`);
}
return canonicalDigest(registry);
}
function validateSurfaces(value, path, errors) {
if (!Array.isArray(value) || value.length === 0 || value.length > SURFACES.size) {
errors.push(`${path}.allowedSurfaces_must_be_nonempty_bounded_array`);
return;
}
if (new Set(value).size !== value.length) errors.push(`${path}.allowedSurfaces_must_be_unique`);
value.forEach((surface) => {
if (!SURFACES.has(surface)) errors.push(`${path}.allowedSurfaces_contains_invalid_surface`);
});
}
function validateSemanticReading(value, path, errors) {
if (value === undefined) return;
if (!isPlainObject(value)) {
errors.push(`${path}.semanticReading_must_be_object`);
return;
}
rejectUnknownKeys(value, READING_KEYS, `${path}.semanticReading`, errors);
requiredIdentifier(value.id, `${path}.semanticReading.id`, errors);
if (!LABEL_SOURCES.has(value.labelSource)) {
errors.push(`${path}.semanticReading.labelSource_invalid`);
}
if (value.labelSource === "registry"
&& (typeof value.label !== "string" || !value.label.trim() || value.label.length > 160)) {
errors.push(`${path}.semanticReading.label_invalid`);
}
if (value.labelSource === "provider_configured"
&& value.label !== undefined
&& (typeof value.label !== "string" || !value.label.trim() || value.label.length > 160)) {
errors.push(`${path}.semanticReading.label_invalid`);
}
if (!UNIT_SOURCES.has(value.unitSource)) {
errors.push(`${path}.semanticReading.unitSource_invalid`);
}
if (value.unitSource === "registry"
&& (typeof value.unit !== "string" || !value.unit.trim() || value.unit.length > 32)) {
errors.push(`${path}.semanticReading.unit_invalid`);
}
if (value.unitSource === "none" && value.unit !== undefined) {
errors.push(`${path}.semanticReading.unit_not_allowed`);
}
if (value.unit !== undefined && (typeof value.unit !== "string" || value.unit.length > 32)) {
errors.push(`${path}.semanticReading.unit_invalid`);
}
}
function canonicalDigest(value) {
return `sha256:${createHash("sha256").update(stableJson(value), "utf8").digest("hex")}`;
}
function stableJson(value) {
return JSON.stringify(stableValue(value));
}
function stableValue(value) {
if (Array.isArray(value)) return value.map(stableValue);
if (!isPlainObject(value)) return value;
return Object.fromEntries(
Object.keys(value)
.filter((key) => value[key] !== undefined)
.sort()
.map((key) => [key, stableValue(value[key])]),
);
}
function rejectUnknownKeys(value, allowed, path, errors) {
for (const key of Object.keys(value)) {
if (!allowed.has(key)) errors.push(`${path}.${key}_not_allowed`);
}
}
function requiredIdentifier(value, path, errors) {
if (typeof value !== "string" || !IDENTIFIER.test(value)) errors.push(`${path}_invalid`);
}
function requiredSemver(value, path, errors) {
if (typeof value !== "string" || !SEMVER.test(value)) errors.push(`${path}_invalid`);
}
function result(errors) {
return { ok: errors.length === 0, errors: [...new Set(errors)] };
}
function isPlainObject(value) {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function deepFreeze(input) {
if (!input || typeof input !== "object" || Object.isFrozen(input)) return input;
Object.freeze(input);
for (const child of Object.values(input)) deepFreeze(child);
return input;
}

View File

@ -0,0 +1,121 @@
import assert from "node:assert/strict";
import {
L2_EXECUTION_PLAN_SCHEMA_VERSION,
L2_MATERIALIZATION_RECEIPT_SCHEMA_VERSION,
attestL2ExecutionPlanMaterialization,
compileL2ExecutionPlan,
instantiateL2Connection,
validateL2ExecutionPlan,
} from "../src/index.mjs";
import {
geliosProviderPackageV8,
geliosTelemetryFieldRegistryV1,
} from "../providers/gelios/v8/index.mjs";
const positionsConnection = instantiateL2Connection(geliosProviderPackageV8, {
tenantId: "tenant-compiler-fixture",
connectionId: "gelios-compiler-fixture",
collectionProfileId: "gelios.positions.current.realtime.v7",
providerCredentialRef: "ndc-credref:provider-compiler-fixture-0001",
});
const positionsPlan = compileL2ExecutionPlan(
geliosProviderPackageV8,
positionsConnection,
{ telemetryFieldRegistry: geliosTelemetryFieldRegistryV1 },
);
assert.equal(positionsPlan.schemaVersion, L2_EXECUTION_PLAN_SCHEMA_VERSION);
assert.deepEqual(validateL2ExecutionPlan(positionsPlan), { ok: true, errors: [] });
assert.match(positionsPlan.executionPlanDigest, /^sha256:[a-f0-9]{64}$/);
assert.match(positionsPlan.artifacts.packageDigest, /^sha256:[a-f0-9]{64}$/);
assert.match(positionsPlan.artifacts.mappingContractDigest, /^sha256:[a-f0-9]{64}$/);
assert.match(positionsPlan.artifacts.telemetryRegistryDigest, /^sha256:[a-f0-9]{64}$/);
assert.deepEqual(positionsPlan.steps.map((step) => step.kind), [
"collection_trigger",
"provider_request",
"provider_request",
"extract_items",
"semantic_mapping",
"data_product_publish",
]);
const unitsRequest = positionsPlan.steps.find(
(step) => step.kind === "provider_request" && step.config.capabilityId === "gelios.units.current.read",
);
assert.deepEqual(unitsRequest.config.request.query, {
incltrip: "true",
inclcntrs: "true",
inclsnsrs: "true",
incllsv: "true",
});
const semanticMapping = positionsPlan.steps.find((step) => step.kind === "semantic_mapping");
assert.deepEqual(
semanticMapping.config.telemetryProjection.entries.map((entry) => entry.reading.id),
["sensor.param.in0", "sensor.param.in1"],
);
assert.equal(Object.isFrozen(positionsPlan), true);
assert.equal(Object.isFrozen(positionsPlan.steps), true);
const clonedPlan = compileL2ExecutionPlan(
structuredClone(geliosProviderPackageV8),
structuredClone(positionsConnection),
{ telemetryFieldRegistry: structuredClone(geliosTelemetryFieldRegistryV1) },
);
assert.equal(clonedPlan.executionPlanDigest, positionsPlan.executionPlanDigest);
assert.throws(
() => compileL2ExecutionPlan(geliosProviderPackageV8, positionsConnection),
/telemetry_registry_required/,
);
const profileConnection = instantiateL2Connection(geliosProviderPackageV8, {
tenantId: "tenant-compiler-fixture",
connectionId: "gelios-profile-compiler-fixture",
collectionProfileId: "gelios.units.profile.cold.v1",
providerCredentialRef: "ndc-credref:provider-compiler-fixture-0001",
});
const profilePlan = compileL2ExecutionPlan(geliosProviderPackageV8, profileConnection);
assert.deepEqual(validateL2ExecutionPlan(profilePlan), { ok: true, errors: [] });
assert.equal(profilePlan.artifacts.telemetryRegistryDigest, undefined);
assert.notEqual(profilePlan.executionPlanDigest, positionsPlan.executionPlanDigest);
const secondConnection = instantiateL2Connection(geliosProviderPackageV8, {
tenantId: "tenant-compiler-fixture",
connectionId: "gelios-compiler-fixture-two",
collectionProfileId: "gelios.positions.current.realtime.v7",
providerCredentialRef: "ndc-credref:provider-compiler-fixture-0002",
});
const secondPlan = compileL2ExecutionPlan(
geliosProviderPackageV8,
secondConnection,
{ telemetryFieldRegistry: geliosTelemetryFieldRegistryV1 },
);
assert.notEqual(secondPlan.executionPlanDigest, positionsPlan.executionPlanDigest);
const receipt = attestL2ExecutionPlanMaterialization(positionsPlan, {
graphRevision: "engine-revision-fixture-0001",
graphDigest: "a".repeat(64),
materializedStepIds: positionsPlan.steps.map((step) => step.id),
});
assert.equal(receipt.schemaVersion, L2_MATERIALIZATION_RECEIPT_SCHEMA_VERSION);
assert.equal(receipt.executionPlanDigest, positionsPlan.executionPlanDigest);
assert.equal(receipt.graphDigest, `sha256:${"a".repeat(64)}`);
assert.match(receipt.receiptDigest, /^sha256:[a-f0-9]{64}$/);
assert.equal(Object.isFrozen(receipt), true);
assert.throws(() => attestL2ExecutionPlanMaterialization(positionsPlan, {
graphRevision: "engine-revision-fixture-0001",
graphDigest: "a".repeat(64),
materializedStepIds: positionsPlan.steps.slice(1).map((step) => step.id),
}), /materializedStepIds_must_exactly_match/);
const tamperedPlan = structuredClone(positionsPlan);
tamperedPlan.steps[0].config.mode = "history";
assert.equal(
validateL2ExecutionPlan(tamperedPlan).errors.includes("executionPlan.executionPlanDigest_mismatch"),
true,
);
const serialized = JSON.stringify(positionsPlan);
assert.equal(serialized.includes("Bearer "), false);
assert.equal(serialized.includes("access_token"), false);
assert.equal(serialized.includes("refresh_token"), false);

View File

@ -0,0 +1,80 @@
import assert from "node:assert/strict";
import {
TELEMETRY_FIELD_PROJECTION_SCHEMA_VERSION,
compileTelemetryFieldProjection,
digestTelemetryFieldRegistry,
validateTelemetryFieldRegistry,
} from "../src/index.mjs";
import {
geliosTelemetryFieldRegistryV1,
} from "../providers/gelios/v8/index.mjs";
assert.deepEqual(validateTelemetryFieldRegistry(geliosTelemetryFieldRegistryV1), {
ok: true,
errors: [],
});
assert.match(digestTelemetryFieldRegistry(geliosTelemetryFieldRegistryV1), /^sha256:[a-f0-9]{64}$/);
const projection = compileTelemetryFieldProjection(geliosTelemetryFieldRegistryV1, {
providerPackageId: "gelios.provider.v8",
providerPackageVersion: "8.0.0",
dataProductId: "fleet.positions.current.v5",
surface: "data_product",
});
assert.equal(projection.schemaVersion, TELEMETRY_FIELD_PROJECTION_SCHEMA_VERSION);
assert.deepEqual(projection.entries.map((entry) => entry.sourceKey), ["in0", "in1"]);
assert.deepEqual(projection.entries.map((entry) => entry.reading.id), [
"sensor.param.in0",
"sensor.param.in1",
]);
assert.equal(projection.entries.some((entry) => entry.sourceKey === "in_0"), false);
assert.equal(Object.isFrozen(projection), true);
assert.equal(Object.isFrozen(projection.entries), true);
const observedExposed = structuredClone(geliosTelemetryFieldRegistryV1);
observedExposed.entries.find((entry) => entry.sourceKey === "in_0").allowedSurfaces.push("foundry");
assert.equal(
validateTelemetryFieldRegistry(observedExposed).errors.includes(
"registry.entries[2].nonapproved_surface_must_be_audit_only",
),
true,
);
const approvedUnclassified = structuredClone(geliosTelemetryFieldRegistryV1);
approvedUnclassified.entries[0].sensitivity = "unclassified";
assert.equal(
validateTelemetryFieldRegistry(approvedUnclassified).errors.includes(
"registry.entries[0].approved_must_be_operational",
),
true,
);
const wildcardSource = structuredClone(geliosTelemetryFieldRegistryV1);
wildcardSource.entries[0].sourceKey = "*";
assert.equal(
validateTelemetryFieldRegistry(wildcardSource).errors.includes(
"registry.entries[0].sourceKey_invalid",
),
true,
);
const duplicateReading = structuredClone(geliosTelemetryFieldRegistryV1);
duplicateReading.entries[1].semanticReading.id = duplicateReading.entries[0].semanticReading.id;
assert.equal(
validateTelemetryFieldRegistry(duplicateReading).errors.includes(
"registry.entries[1].semanticReading.id_must_be_unique",
),
true,
);
assert.throws(() => compileTelemetryFieldProjection(geliosTelemetryFieldRegistryV1, {
providerPackageId: "another.provider.v1",
providerPackageVersion: "8.0.0",
dataProductId: "fleet.positions.current.v5",
surface: "data_product",
}), /providerPackageId_mismatch/);
const serialized = JSON.stringify(geliosTelemetryFieldRegistryV1);
for (const forbidden of ["phone", "imei", "commands", "access_token", "refresh_token"]) {
assert.equal(serialized.toLowerCase().includes(forbidden), false);
}