@tenova/swt3-ai 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -13
- package/dist/adapters/a2a.d.ts +26 -0
- package/dist/adapters/a2a.d.ts.map +1 -0
- package/dist/adapters/a2a.js +86 -0
- package/dist/adapters/a2a.js.map +1 -0
- package/dist/adapters/crewai.d.ts +26 -0
- package/dist/adapters/crewai.d.ts.map +1 -0
- package/dist/adapters/crewai.js +78 -0
- package/dist/adapters/crewai.js.map +1 -0
- package/dist/adapters/foundry.d.ts +26 -0
- package/dist/adapters/foundry.d.ts.map +1 -0
- package/dist/adapters/foundry.js +118 -0
- package/dist/adapters/foundry.js.map +1 -0
- package/dist/adapters/google-adk.d.ts +25 -0
- package/dist/adapters/google-adk.d.ts.map +1 -0
- package/dist/adapters/google-adk.js +81 -0
- package/dist/adapters/google-adk.js.map +1 -0
- package/dist/chain.d.ts +33 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +94 -0
- package/dist/chain.js.map +1 -0
- package/dist/clearing.d.ts +7 -2
- package/dist/clearing.d.ts.map +1 -1
- package/dist/clearing.js +16 -5
- package/dist/clearing.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +123 -6
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -1
- package/dist/demo.d.ts.map +1 -1
- package/dist/demo.js +173 -10
- package/dist/demo.js.map +1 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +39 -16
- package/dist/doctor.js.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/procedures.d.ts +20 -0
- package/dist/procedures.d.ts.map +1 -0
- package/dist/procedures.js +162 -0
- package/dist/procedures.js.map +1 -0
- package/dist/profile.d.ts +50 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +109 -0
- package/dist/profile.js.map +1 -0
- package/dist/trust.d.ts +96 -2
- package/dist/trust.d.ts.map +1 -1
- package/dist/trust.js +200 -9
- package/dist/trust.js.map +1 -1
- package/dist/types.d.ts +120 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +173 -1
- package/dist/types.js.map +1 -1
- package/dist/witness.d.ts +547 -0
- package/dist/witness.d.ts.map +1 -1
- package/dist/witness.js +1557 -202
- package/dist/witness.js.map +1 -1
- package/package.json +2 -2
- package/templates/healthcare-clinical.yaml +4 -0
package/dist/witness.js
CHANGED
|
@@ -34,7 +34,7 @@ import { queryHardware as queryHw, topologyCode as topoCode, queryTPM as queryTp
|
|
|
34
34
|
import { queryEnvironment as queryEnv } from "./environment.js";
|
|
35
35
|
import { TrustRegistry, verifyCredential, signCredential, TRUST_LEVEL_NAMES, } from "./trust.js";
|
|
36
36
|
import { createVercelOnFinish } from "./adapters/vercel-ai.js";
|
|
37
|
-
import { QUANTIZATION_CODES, POLICY_CATEGORIES, BINDING_METHODS, APPROVAL_STATUS, PII_EVENT_TYPES, CONTENT_TYPE_CODES, BASELINE_MODE_CODES } from "./types.js";
|
|
37
|
+
import { QUANTIZATION_CODES, POLICY_CATEGORIES, BINDING_METHODS, APPROVAL_STATUS, PII_EVENT_TYPES, CONTENT_TYPE_CODES, BASELINE_MODE_CODES, LICENSE_TYPE_CODES, SBOM_FORMAT_CODES, REDTEAM_CATEGORY_CODES, CONSENT_BASIS_CODES, DRIFT_TYPE_CODES, LOG_FORMAT_CODES, INCIDENT_TYPE_CODES, BENCHMARK_TYPE_CODES, PERTURBATION_TYPE_CODES, CYBER_FRAMEWORK_CODES, DISCLOSURE_TYPE_CODES, RECIPIENT_TYPE_CODES, DETECTION_METHOD_CODES, PROCESSING_TYPE_CODES, DECISION_TYPE_CODES, REPORTING_STATUS_CODES, SUPPLY_RISK_CODES, PMM_TYPE_CODES, LIFECYCLE_STAGE_CODES, METAGOV_SCOPE_CODES, METAGOV_PERMISSION_CODES, METAGOV_OVERRIDE_REASON_CODES, METAGOV_REVIEW_STATUS_CODES, METAGOV_DIVERGENCE_CODES, DESIGN_DOMAIN_CODES, SIMULATION_TYPE_CODES, APPROVAL_TYPE_CODES, MATERIAL_STANDARD_CODES, CHAIN_STATUS_CODES, RELEASE_TYPE_CODES } from "./types.js";
|
|
38
38
|
import { loadFullConfig, validatePolicy } from "./config.js";
|
|
39
39
|
import { MerkleAccumulator } from "./merkle.js";
|
|
40
40
|
// ── Chain Density Enforcement ──────────────────────────────────────────
|
|
@@ -44,6 +44,13 @@ function globToRegex(pattern) {
|
|
|
44
44
|
.replace(/\?/g, ".");
|
|
45
45
|
return new RegExp("^" + escaped + "$");
|
|
46
46
|
}
|
|
47
|
+
/** Parse first 8 hex chars as int mod 1M, falling back to hash for non-hex input. */
|
|
48
|
+
function safeHexInt(s) {
|
|
49
|
+
const v = parseInt(s.slice(0, 8), 16);
|
|
50
|
+
if (!isNaN(v))
|
|
51
|
+
return v % 1000000;
|
|
52
|
+
return parseInt(sha256Truncated(s, 8), 16) % 1000000;
|
|
53
|
+
}
|
|
47
54
|
function parseVelocity(spec) {
|
|
48
55
|
const parts = spec.split("/");
|
|
49
56
|
const limit = parseInt(parts[0], 10);
|
|
@@ -297,6 +304,7 @@ export class Witness {
|
|
|
297
304
|
_chainEnforcer;
|
|
298
305
|
_sentinel;
|
|
299
306
|
_sentinelDetecting = false;
|
|
307
|
+
_lastKnownGoodVersion = 0;
|
|
300
308
|
get configHash() {
|
|
301
309
|
return this._configHash;
|
|
302
310
|
}
|
|
@@ -975,8 +983,9 @@ export class Witness {
|
|
|
975
983
|
? Witness.hashModelFile(weights)
|
|
976
984
|
: weights;
|
|
977
985
|
const match = options?.expectedHash ? info.fileHash === options.expectedHash : true;
|
|
986
|
+
const stageCode = options?.lifecycleStage ? (LIFECYCLE_STAGE_CODES[options.lifecycleStage] ?? 0) : 0;
|
|
978
987
|
const [ts, epoch] = timestampMs();
|
|
979
|
-
const fa = 1, fb = match ? 1 : 0, fc =
|
|
988
|
+
const fa = 1, fb = match ? 1 : 0, fc = stageCode;
|
|
980
989
|
const fp = mintFingerprint(this.config.tenantId, "AI-MDL.5", fa, fb, fc, ts);
|
|
981
990
|
const payload = {
|
|
982
991
|
procedure_id: "AI-MDL.5", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
@@ -994,6 +1003,8 @@ export class Witness {
|
|
|
994
1003
|
ctx.format = info.format;
|
|
995
1004
|
if (options?.expectedHash)
|
|
996
1005
|
ctx.expected_hash = options.expectedHash;
|
|
1006
|
+
if (options?.lifecycleStage)
|
|
1007
|
+
ctx.lifecycle_stage = options.lifecycleStage;
|
|
997
1008
|
payload.ai_context = ctx;
|
|
998
1009
|
}
|
|
999
1010
|
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
@@ -1548,206 +1559,338 @@ export class Witness {
|
|
|
1548
1559
|
this.buffer.enqueueMany([payload]);
|
|
1549
1560
|
return payload;
|
|
1550
1561
|
}
|
|
1551
|
-
// ──
|
|
1562
|
+
// ── License Provenance (AI-LIC.1) ──────────────────────────────
|
|
1552
1563
|
/**
|
|
1553
|
-
* Witness a
|
|
1564
|
+
* Witness license provenance of a model stack (AI-LIC.1).
|
|
1565
|
+
*
|
|
1566
|
+
* Records the license composition of base models, adapters, and
|
|
1567
|
+
* training data. Detects license drift when components from
|
|
1568
|
+
* different license families are combined.
|
|
1569
|
+
*
|
|
1570
|
+
* @param options.componentsChecked - Number of license components verified.
|
|
1571
|
+
* @param options.allCompliant - True if all components are license-compatible.
|
|
1572
|
+
* @param options.licenseType - Primary license type (permissive, copyleft, proprietary, dual, openmdw, unknown).
|
|
1573
|
+
* @param options.baseModelLicense - SPDX identifier of the base model license.
|
|
1574
|
+
* @param options.adapterLicenses - List of adapter/LoRA license identifiers.
|
|
1575
|
+
* @param options.spdxIds - SPDX identifiers for all components.
|
|
1576
|
+
* @param options.licenseHash - SHA-256 of the full license manifest.
|
|
1554
1577
|
*/
|
|
1555
|
-
|
|
1556
|
-
const
|
|
1578
|
+
witnessLicenseProvenance(options) {
|
|
1579
|
+
const fa = options.componentsChecked;
|
|
1580
|
+
const fb = options.allCompliant ? 1 : 0;
|
|
1581
|
+
const fc = LICENSE_TYPE_CODES[options.licenseType] ?? 5;
|
|
1557
1582
|
const [ts, epoch] = timestampMs();
|
|
1558
|
-
const
|
|
1559
|
-
const fb = this.config.cycleId ? 1 : 0;
|
|
1560
|
-
const fc = accepted ? 1 : 0;
|
|
1561
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-CHAIN.1", fa, fb, fc, ts);
|
|
1583
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-LIC.1", fa, fb, fc, ts);
|
|
1562
1584
|
const payload = {
|
|
1563
|
-
procedure_id: "AI-
|
|
1585
|
+
procedure_id: "AI-LIC.1",
|
|
1586
|
+
factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1564
1587
|
clearing_level: this.config.clearingLevel,
|
|
1565
1588
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1566
1589
|
};
|
|
1567
1590
|
if (this.config.clearingLevel <= 1) {
|
|
1568
|
-
payload.ai_model_id =
|
|
1569
|
-
|
|
1570
|
-
provider: "
|
|
1591
|
+
payload.ai_model_id = `license-${options.licenseType}`;
|
|
1592
|
+
const ctx = {
|
|
1593
|
+
provider: "license-provenance",
|
|
1594
|
+
license_type: options.licenseType,
|
|
1571
1595
|
};
|
|
1596
|
+
if (options.baseModelLicense)
|
|
1597
|
+
ctx.base_model_license = options.baseModelLicense;
|
|
1598
|
+
if (options.adapterLicenses)
|
|
1599
|
+
ctx.adapter_licenses = options.adapterLicenses;
|
|
1600
|
+
if (options.spdxIds)
|
|
1601
|
+
ctx.spdx_ids = options.spdxIds;
|
|
1602
|
+
if (options.licenseHash)
|
|
1603
|
+
ctx.license_hash = options.licenseHash;
|
|
1604
|
+
payload.ai_context = ctx;
|
|
1572
1605
|
}
|
|
1573
|
-
const policyHash = this.config.policyVersion
|
|
1606
|
+
const policyHash = this.config.policyVersion
|
|
1607
|
+
? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
1574
1608
|
this._applyOperationalMetadata(payload, policyHash);
|
|
1575
1609
|
this.buffer.enqueueMany([payload]);
|
|
1576
1610
|
return payload;
|
|
1577
1611
|
}
|
|
1612
|
+
// ── AI Bill of Materials (AI-SBOM.1) ────────────────────────────
|
|
1578
1613
|
/**
|
|
1579
|
-
* Witness
|
|
1614
|
+
* Witness an AI bill of materials snapshot (AI-SBOM.1).
|
|
1580
1615
|
*
|
|
1581
|
-
*
|
|
1582
|
-
*
|
|
1583
|
-
*
|
|
1616
|
+
* Records the component inventory of an AI system at build or deploy
|
|
1617
|
+
* time, covering models, datasets, infrastructure, and security
|
|
1618
|
+
* posture per G7/CISA "SBOM for AI Minimum Elements" (May 2026).
|
|
1584
1619
|
*
|
|
1585
|
-
*
|
|
1586
|
-
*
|
|
1620
|
+
* @param options.totalComponents - Number of components in the BOM.
|
|
1621
|
+
* @param options.clustersDocumented - G7 clusters documented (0-7).
|
|
1622
|
+
* @param options.format - BOM format (cyclonedx, spdx, custom, unknown).
|
|
1623
|
+
* @param options.bomHash - SHA-256 of the full BOM document.
|
|
1624
|
+
* @param options.version - BOM version string.
|
|
1625
|
+
* @param options.modelCount - Number of AI models in BOM.
|
|
1626
|
+
* @param options.datasetCount - Number of datasets in BOM.
|
|
1627
|
+
* @param options.infrastructureComponents - Number of infra components.
|
|
1587
1628
|
*/
|
|
1588
|
-
|
|
1589
|
-
const
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
: targetTrustLevel;
|
|
1593
|
-
this._chainTrustLevels.push(targetTrustLevel);
|
|
1594
|
-
const effectiveTrustLevel = Math.min(...this._chainTrustLevels);
|
|
1595
|
-
// Enforce minimum if configured
|
|
1596
|
-
const minLevel = this.config.chainMinTrustLevel;
|
|
1597
|
-
if (minLevel !== undefined && effectiveTrustLevel < minLevel && this._strict) {
|
|
1598
|
-
throw new ChainTrustError(effectiveTrustLevel, minLevel);
|
|
1599
|
-
}
|
|
1629
|
+
witnessSbom(options) {
|
|
1630
|
+
const fa = options.totalComponents;
|
|
1631
|
+
const fb = options.clustersDocumented;
|
|
1632
|
+
const fc = SBOM_FORMAT_CODES[options.format] ?? 3;
|
|
1600
1633
|
const [ts, epoch] = timestampMs();
|
|
1601
|
-
const
|
|
1602
|
-
const fb = targetTrustLevel;
|
|
1603
|
-
const fc = effectiveTrustLevel;
|
|
1604
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-CHAIN.1", fa, fb, fc, ts);
|
|
1634
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-SBOM.1", fa, fb, fc, ts);
|
|
1605
1635
|
const payload = {
|
|
1606
|
-
procedure_id: "AI-
|
|
1636
|
+
procedure_id: "AI-SBOM.1",
|
|
1637
|
+
factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1607
1638
|
clearing_level: this.config.clearingLevel,
|
|
1608
1639
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1609
1640
|
};
|
|
1610
1641
|
if (this.config.clearingLevel <= 1) {
|
|
1611
|
-
payload.ai_model_id =
|
|
1612
|
-
|
|
1613
|
-
provider: "
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
effective_trust_level: effectiveTrustLevel,
|
|
1617
|
-
chain_depth: this._chainTrustLevels.length,
|
|
1642
|
+
payload.ai_model_id = `sbom-${options.format}`;
|
|
1643
|
+
const ctx = {
|
|
1644
|
+
provider: "ai-sbom",
|
|
1645
|
+
bom_hash: options.bomHash,
|
|
1646
|
+
format: options.format,
|
|
1618
1647
|
};
|
|
1648
|
+
if (options.version)
|
|
1649
|
+
ctx.version = options.version;
|
|
1650
|
+
if (options.modelCount != null)
|
|
1651
|
+
ctx.model_count = options.modelCount;
|
|
1652
|
+
if (options.datasetCount != null)
|
|
1653
|
+
ctx.dataset_count = options.datasetCount;
|
|
1654
|
+
if (options.infrastructureComponents != null)
|
|
1655
|
+
ctx.infrastructure_components = options.infrastructureComponents;
|
|
1656
|
+
payload.ai_context = ctx;
|
|
1619
1657
|
}
|
|
1620
|
-
const
|
|
1621
|
-
|
|
1658
|
+
const policyHash = this.config.policyVersion
|
|
1659
|
+
? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
1622
1660
|
this._applyOperationalMetadata(payload, policyHash);
|
|
1623
|
-
|
|
1624
|
-
payload.cycle_id = cycleId;
|
|
1625
|
-
const payloads = [payload];
|
|
1626
|
-
// Auto-mint AI-CHAIN.2 if trust degraded
|
|
1627
|
-
if (this._chainTrustLevels.length > 1 && effectiveTrustLevel < prevEffective) {
|
|
1628
|
-
const degradation = extractChainTrustDegradationPayload(this.config.tenantId, prevEffective, effectiveTrustLevel, this.config.clearingLevel, this.config.agentId, this.config.signingKey, this.config.signingKeyId, this.config.signingKeyVersion, cycleId, policyHash, this.config.signingAlgorithm);
|
|
1629
|
-
if (this.config.clearingLevel <= 1) {
|
|
1630
|
-
degradation.ai_context = {
|
|
1631
|
-
provider: "chain-trust-degradation",
|
|
1632
|
-
previous_effective: prevEffective,
|
|
1633
|
-
new_effective: effectiveTrustLevel,
|
|
1634
|
-
target_agent: targetAgentId,
|
|
1635
|
-
};
|
|
1636
|
-
}
|
|
1637
|
-
payloads.push(degradation);
|
|
1638
|
-
}
|
|
1639
|
-
this.buffer.enqueueMany(payloads);
|
|
1661
|
+
this.buffer.enqueueMany([payload]);
|
|
1640
1662
|
return payload;
|
|
1641
1663
|
}
|
|
1642
|
-
|
|
1643
|
-
get chainEffectiveTrustLevel() {
|
|
1644
|
-
return this._chainTrustLevels.length === 0 ? 4 : Math.min(...this._chainTrustLevels);
|
|
1645
|
-
}
|
|
1646
|
-
/** The trust levels recorded at each chain handoff. */
|
|
1647
|
-
get chainTrustLevels() {
|
|
1648
|
-
return this._chainTrustLevels;
|
|
1649
|
-
}
|
|
1664
|
+
// ── Adversarial Test Campaign (AI-REDTEAM.1) ──────────────────
|
|
1650
1665
|
/**
|
|
1651
|
-
* Witness
|
|
1666
|
+
* Witness an adversarial test campaign (AI-REDTEAM.1).
|
|
1667
|
+
*
|
|
1668
|
+
* Records red team or adversarial testing results, transforming
|
|
1669
|
+
* point-in-time reports into continuous verifiable evidence per
|
|
1670
|
+
* EO 14110, EU AI Act Art. 9(7), and NIST AI 100-2.
|
|
1671
|
+
*
|
|
1672
|
+
* @param options.testsExecuted - Number of attack scenarios run.
|
|
1673
|
+
* @param options.testsPassed - Number of attacks successfully mitigated.
|
|
1674
|
+
* @param options.coverageCategory - Coverage category key (prompt_injection, jailbreak, etc.).
|
|
1675
|
+
* @param options.framework - Testing framework (e.g. "OWASP-LLM-Top10", "NIST-AI-100-2").
|
|
1676
|
+
* @param options.campaignId - Unique identifier for this test campaign.
|
|
1677
|
+
* @param options.modelUnderTest - Model identifier being tested.
|
|
1678
|
+
* @param options.attackTaxonomy - Attack taxonomy version or reference.
|
|
1679
|
+
* @param options.passRate - Computed pass rate (0-1).
|
|
1680
|
+
* @param options.durationSeconds - Campaign duration in seconds.
|
|
1652
1681
|
*/
|
|
1653
|
-
|
|
1654
|
-
const
|
|
1655
|
-
const
|
|
1682
|
+
witnessRedTeam(options) {
|
|
1683
|
+
const fa = options.testsExecuted;
|
|
1684
|
+
const fb = options.testsPassed;
|
|
1685
|
+
const fc = REDTEAM_CATEGORY_CODES[options.coverageCategory] ?? 10;
|
|
1656
1686
|
const [ts, epoch] = timestampMs();
|
|
1657
|
-
const
|
|
1658
|
-
const fb = autoDetected ? 1 : 0;
|
|
1659
|
-
const fc = POLICY_CATEGORIES[policyCategory] ?? 0;
|
|
1660
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-VIO.1", fa, fb, fc, ts);
|
|
1687
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-REDTEAM.1", fa, fb, fc, ts);
|
|
1661
1688
|
const payload = {
|
|
1662
|
-
procedure_id: "AI-
|
|
1689
|
+
procedure_id: "AI-REDTEAM.1",
|
|
1690
|
+
factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1663
1691
|
clearing_level: this.config.clearingLevel,
|
|
1664
1692
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1665
1693
|
};
|
|
1666
1694
|
if (this.config.clearingLevel <= 1) {
|
|
1667
|
-
payload.ai_model_id = `
|
|
1668
|
-
|
|
1669
|
-
provider: "
|
|
1695
|
+
payload.ai_model_id = `redteam-${options.coverageCategory}`;
|
|
1696
|
+
const ctx = {
|
|
1697
|
+
provider: "red-team",
|
|
1698
|
+
coverage_category: options.coverageCategory,
|
|
1670
1699
|
};
|
|
1700
|
+
if (options.framework)
|
|
1701
|
+
ctx.framework = options.framework;
|
|
1702
|
+
if (options.campaignId)
|
|
1703
|
+
ctx.campaign_id = options.campaignId;
|
|
1704
|
+
if (options.modelUnderTest)
|
|
1705
|
+
ctx.model_under_test = options.modelUnderTest;
|
|
1706
|
+
if (options.attackTaxonomy)
|
|
1707
|
+
ctx.attack_taxonomy = options.attackTaxonomy;
|
|
1708
|
+
if (options.passRate != null)
|
|
1709
|
+
ctx.pass_rate = options.passRate;
|
|
1710
|
+
if (options.durationSeconds != null)
|
|
1711
|
+
ctx.duration_seconds = options.durationSeconds;
|
|
1712
|
+
payload.ai_context = ctx;
|
|
1671
1713
|
}
|
|
1672
|
-
const policyHash = this.config.policyVersion
|
|
1714
|
+
const policyHash = this.config.policyVersion
|
|
1715
|
+
? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
1673
1716
|
this._applyOperationalMetadata(payload, policyHash);
|
|
1674
1717
|
this.buffer.enqueueMany([payload]);
|
|
1675
1718
|
return payload;
|
|
1676
1719
|
}
|
|
1720
|
+
// ── Data Subject Consent (AI-CONSENT.1) ───────────────────────
|
|
1677
1721
|
/**
|
|
1678
|
-
* Witness
|
|
1722
|
+
* Witness data subject consent documentation (AI-CONSENT.1).
|
|
1723
|
+
*
|
|
1724
|
+
* Records that consent or lawful basis was documented before
|
|
1725
|
+
* processing. Complements CJT fields (which declare legal basis)
|
|
1726
|
+
* by proving consent was actually obtained per GDPR Art. 6/7
|
|
1727
|
+
* and EU AI Act Art. 10.
|
|
1728
|
+
*
|
|
1729
|
+
* @param options.subjectsCovered - Number of data subjects in scope.
|
|
1730
|
+
* @param options.legalBasisType - GDPR lawful basis (consent, contract, legal_obligation, vital_interest, public_task, legitimate_interest).
|
|
1731
|
+
* @param options.withdrawalAvailable - True if withdrawal mechanism exists.
|
|
1732
|
+
* @param options.purpose - Processing purpose description.
|
|
1733
|
+
* @param options.retentionDays - Data retention period in days.
|
|
1734
|
+
* @param options.consentMechanism - Mechanism used (e.g. "opt-in-form", "api-consent-endpoint").
|
|
1735
|
+
* @param options.consentHash - SHA-256 of the consent record.
|
|
1736
|
+
* @param options.dataCategories - Categories of personal data processed.
|
|
1679
1737
|
*/
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
const computed = options.charterHash ?? sha256Truncated(options.charterText);
|
|
1685
|
-
const match = options.expectedHash ? computed === options.expectedHash : true;
|
|
1738
|
+
witnessConsent(options) {
|
|
1739
|
+
const fa = options.subjectsCovered;
|
|
1740
|
+
const fb = CONSENT_BASIS_CODES[options.legalBasisType] ?? 0;
|
|
1741
|
+
const fc = options.withdrawalAvailable ? 1 : 0;
|
|
1686
1742
|
const [ts, epoch] = timestampMs();
|
|
1687
|
-
const
|
|
1688
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-CHR.1", fa, fb, fc, ts);
|
|
1743
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-CONSENT.1", fa, fb, fc, ts);
|
|
1689
1744
|
const payload = {
|
|
1690
|
-
procedure_id: "AI-
|
|
1745
|
+
procedure_id: "AI-CONSENT.1",
|
|
1746
|
+
factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1691
1747
|
clearing_level: this.config.clearingLevel,
|
|
1692
1748
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1693
1749
|
};
|
|
1694
1750
|
if (this.config.clearingLevel <= 1) {
|
|
1695
|
-
payload.ai_model_id =
|
|
1696
|
-
const ctx = {
|
|
1697
|
-
|
|
1698
|
-
|
|
1751
|
+
payload.ai_model_id = `consent-${options.legalBasisType}`;
|
|
1752
|
+
const ctx = {
|
|
1753
|
+
provider: "consent-management",
|
|
1754
|
+
legal_basis_type: options.legalBasisType,
|
|
1755
|
+
};
|
|
1756
|
+
if (options.purpose)
|
|
1757
|
+
ctx.purpose = options.purpose;
|
|
1758
|
+
if (options.retentionDays != null)
|
|
1759
|
+
ctx.retention_days = options.retentionDays;
|
|
1760
|
+
if (options.consentMechanism)
|
|
1761
|
+
ctx.consent_mechanism = options.consentMechanism;
|
|
1762
|
+
if (options.consentHash)
|
|
1763
|
+
ctx.consent_hash = options.consentHash;
|
|
1764
|
+
if (options.dataCategories)
|
|
1765
|
+
ctx.data_categories = options.dataCategories;
|
|
1699
1766
|
payload.ai_context = ctx;
|
|
1700
1767
|
}
|
|
1701
|
-
const policyHash = this.config.policyVersion
|
|
1768
|
+
const policyHash = this.config.policyVersion
|
|
1769
|
+
? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
1702
1770
|
this._applyOperationalMetadata(payload, policyHash);
|
|
1703
1771
|
this.buffer.enqueueMany([payload]);
|
|
1704
1772
|
return payload;
|
|
1705
1773
|
}
|
|
1774
|
+
// ── Multi-Agent Delegation (AI-MULTI.1) ───────────────────────
|
|
1706
1775
|
/**
|
|
1707
|
-
* Witness
|
|
1776
|
+
* Witness inter-agent permission delegation (AI-MULTI.1).
|
|
1777
|
+
*
|
|
1778
|
+
* Records the permission envelope when one agent delegates tasks
|
|
1779
|
+
* to another. Complements AI-CHAIN.1/2 (which witness handoffs and
|
|
1780
|
+
* trust degradation) by witnessing WHAT was delegated per
|
|
1781
|
+
* EU AI Act Art. 9 and NIST AI RMF GOVERN 1.3.
|
|
1782
|
+
*
|
|
1783
|
+
* @param options.delegationDepth - Hops from original human authorization.
|
|
1784
|
+
* @param options.permissionsGranted - Count of distinct permissions delegated.
|
|
1785
|
+
* @param options.timeBoundMinutes - Minutes until delegation expires (0 = unbounded).
|
|
1786
|
+
* @param options.parentAgentId - Delegating agent identifier (hashed in context).
|
|
1787
|
+
* @param options.childAgentId - Receiving agent identifier (hashed in context).
|
|
1788
|
+
* @param options.delegatedTools - List of tool names being delegated.
|
|
1789
|
+
* @param options.scopeHash - SHA-256 of the permission manifest.
|
|
1790
|
+
* @param options.authorizationChain - Ordered agent IDs from human to child (each hashed).
|
|
1708
1791
|
*/
|
|
1709
|
-
|
|
1710
|
-
const
|
|
1711
|
-
const
|
|
1792
|
+
witnessMultiAgentDelegation(options) {
|
|
1793
|
+
const fa = options.delegationDepth;
|
|
1794
|
+
const fb = options.permissionsGranted;
|
|
1795
|
+
const fc = options.timeBoundMinutes;
|
|
1712
1796
|
const [ts, epoch] = timestampMs();
|
|
1713
|
-
const
|
|
1714
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-MDL.8", fa, fb, fc, ts);
|
|
1797
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-MULTI.1", fa, fb, fc, ts);
|
|
1715
1798
|
const payload = {
|
|
1716
|
-
procedure_id: "AI-
|
|
1799
|
+
procedure_id: "AI-MULTI.1",
|
|
1800
|
+
factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1717
1801
|
clearing_level: this.config.clearingLevel,
|
|
1718
1802
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1719
1803
|
};
|
|
1720
1804
|
if (this.config.clearingLevel <= 1) {
|
|
1721
|
-
payload.ai_model_id =
|
|
1722
|
-
|
|
1723
|
-
provider: "
|
|
1805
|
+
payload.ai_model_id = `delegation-depth-${options.delegationDepth}`;
|
|
1806
|
+
const ctx = {
|
|
1807
|
+
provider: "multi-agent",
|
|
1808
|
+
parent_agent_hash: sha256Truncated(options.parentAgentId),
|
|
1809
|
+
child_agent_hash: sha256Truncated(options.childAgentId),
|
|
1724
1810
|
};
|
|
1811
|
+
if (options.delegatedTools)
|
|
1812
|
+
ctx.delegated_tools = options.delegatedTools;
|
|
1813
|
+
if (options.scopeHash)
|
|
1814
|
+
ctx.scope_hash = options.scopeHash;
|
|
1815
|
+
if (options.authorizationChain) {
|
|
1816
|
+
ctx.authorization_chain = options.authorizationChain.map((id) => sha256Truncated(id));
|
|
1817
|
+
}
|
|
1818
|
+
payload.ai_context = ctx;
|
|
1819
|
+
}
|
|
1820
|
+
const policyHash = this.config.policyVersion
|
|
1821
|
+
? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
1822
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
1823
|
+
this.buffer.enqueueMany([payload]);
|
|
1824
|
+
return payload;
|
|
1825
|
+
}
|
|
1826
|
+
// ── Model Drift Detection (AI-DRIFT.1) ─────────────────────────
|
|
1827
|
+
/**
|
|
1828
|
+
* Witness model drift detection (AI-DRIFT.1).
|
|
1829
|
+
*
|
|
1830
|
+
* Records statistical drift events including data drift, concept drift,
|
|
1831
|
+
* and prediction drift per EU AI Act Art. 9(2)(b) continuous risk
|
|
1832
|
+
* estimation and NIST AI RMF MEASURE 2.6.
|
|
1833
|
+
*/
|
|
1834
|
+
witnessDrift(options) {
|
|
1835
|
+
const fa = options.metricsEvaluated;
|
|
1836
|
+
const fb = options.driftedCount;
|
|
1837
|
+
const fc = DRIFT_TYPE_CODES[options.driftType] ?? 0;
|
|
1838
|
+
const [ts, epoch] = timestampMs();
|
|
1839
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-DRIFT.1", fa, fb, fc, ts);
|
|
1840
|
+
const payload = {
|
|
1841
|
+
procedure_id: "AI-DRIFT.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1842
|
+
clearing_level: this.config.clearingLevel,
|
|
1843
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1844
|
+
};
|
|
1845
|
+
if (this.config.clearingLevel <= 1) {
|
|
1846
|
+
payload.ai_model_id = `drift-${options.driftType}`;
|
|
1847
|
+
const ctx = { provider: "drift-detection", drift_type: options.driftType };
|
|
1848
|
+
if (options.baselineHash)
|
|
1849
|
+
ctx.baseline_hash = options.baselineHash;
|
|
1850
|
+
if (options.driftScore != null)
|
|
1851
|
+
ctx.drift_score = options.driftScore;
|
|
1852
|
+
if (options.detectionMethod)
|
|
1853
|
+
ctx.detection_method = options.detectionMethod;
|
|
1854
|
+
if (options.windowSize != null)
|
|
1855
|
+
ctx.window_size = options.windowSize;
|
|
1856
|
+
if (options.threshold != null)
|
|
1857
|
+
ctx.threshold = options.threshold;
|
|
1858
|
+
payload.ai_context = ctx;
|
|
1725
1859
|
}
|
|
1726
1860
|
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
1727
1861
|
this._applyOperationalMetadata(payload, policyHash);
|
|
1728
1862
|
this.buffer.enqueueMany([payload]);
|
|
1729
1863
|
return payload;
|
|
1730
1864
|
}
|
|
1865
|
+
// ── Audit Log Integrity (AI-AUDIT.1) ──────────────────────────
|
|
1731
1866
|
/**
|
|
1732
|
-
* Witness
|
|
1867
|
+
* Witness audit log integrity verification (AI-AUDIT.1).
|
|
1868
|
+
*
|
|
1869
|
+
* Records verification of audit trail integrity per EU AI Act
|
|
1870
|
+
* Art. 12 (record-keeping) and GDPR Art. 30 (ROPA).
|
|
1733
1871
|
*/
|
|
1734
|
-
|
|
1735
|
-
const
|
|
1872
|
+
witnessAuditIntegrity(options) {
|
|
1873
|
+
const fa = options.entriesChecked;
|
|
1874
|
+
const fb = options.integrityVerified ? 1 : 0;
|
|
1875
|
+
const fc = LOG_FORMAT_CODES[options.logFormat] ?? 3;
|
|
1736
1876
|
const [ts, epoch] = timestampMs();
|
|
1737
|
-
const
|
|
1738
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-HITL.3", fa, fb, fc, ts);
|
|
1877
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-AUDIT.1", fa, fb, fc, ts);
|
|
1739
1878
|
const payload = {
|
|
1740
|
-
procedure_id: "AI-
|
|
1879
|
+
procedure_id: "AI-AUDIT.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1741
1880
|
clearing_level: this.config.clearingLevel,
|
|
1742
1881
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1743
1882
|
};
|
|
1744
1883
|
if (this.config.clearingLevel <= 1) {
|
|
1745
|
-
payload.ai_model_id =
|
|
1746
|
-
const ctx = {
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
if (options
|
|
1750
|
-
ctx.
|
|
1884
|
+
payload.ai_model_id = `audit-${options.logFormat}`;
|
|
1885
|
+
const ctx = { provider: "audit-integrity", log_format: options.logFormat };
|
|
1886
|
+
if (options.logHash)
|
|
1887
|
+
ctx.log_hash = options.logHash;
|
|
1888
|
+
if (options.periodStart)
|
|
1889
|
+
ctx.period_start = options.periodStart;
|
|
1890
|
+
if (options.periodEnd)
|
|
1891
|
+
ctx.period_end = options.periodEnd;
|
|
1892
|
+
if (options.gapsDetected != null)
|
|
1893
|
+
ctx.gaps_detected = options.gapsDetected;
|
|
1751
1894
|
payload.ai_context = ctx;
|
|
1752
1895
|
}
|
|
1753
1896
|
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
@@ -1755,27 +1898,35 @@ export class Witness {
|
|
|
1755
1898
|
this.buffer.enqueueMany([payload]);
|
|
1756
1899
|
return payload;
|
|
1757
1900
|
}
|
|
1901
|
+
// ── Incident Reporting (AI-INCIDENT.1) ────────────────────────
|
|
1758
1902
|
/**
|
|
1759
|
-
* Witness
|
|
1903
|
+
* Witness incident reporting (AI-INCIDENT.1).
|
|
1904
|
+
*
|
|
1905
|
+
* Records that a serious incident was reported per EU AI Act
|
|
1906
|
+
* Art. 62 and NIST AI RMF MANAGE 3.2.
|
|
1760
1907
|
*/
|
|
1761
|
-
|
|
1762
|
-
const
|
|
1763
|
-
const
|
|
1908
|
+
witnessIncident(options) {
|
|
1909
|
+
const fa = options.severityCode;
|
|
1910
|
+
const fb = options.authorityNotified ? 1 : 0;
|
|
1911
|
+
const fc = INCIDENT_TYPE_CODES[options.incidentType] ?? 5;
|
|
1764
1912
|
const [ts, epoch] = timestampMs();
|
|
1765
|
-
const
|
|
1766
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-SAFE.1", fa, fb, fc, ts);
|
|
1913
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-INCIDENT.1", fa, fb, fc, ts);
|
|
1767
1914
|
const payload = {
|
|
1768
|
-
procedure_id: "AI-
|
|
1915
|
+
procedure_id: "AI-INCIDENT.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1769
1916
|
clearing_level: this.config.clearingLevel,
|
|
1770
1917
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1771
1918
|
};
|
|
1772
1919
|
if (this.config.clearingLevel <= 1) {
|
|
1773
|
-
payload.ai_model_id =
|
|
1774
|
-
const ctx = {
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
if (options
|
|
1778
|
-
ctx.
|
|
1920
|
+
payload.ai_model_id = `incident-${options.incidentType}`;
|
|
1921
|
+
const ctx = { provider: "incident-reporting", incident_type: options.incidentType };
|
|
1922
|
+
if (options.incidentId)
|
|
1923
|
+
ctx.incident_id = options.incidentId;
|
|
1924
|
+
if (options.authority)
|
|
1925
|
+
ctx.authority = options.authority;
|
|
1926
|
+
if (options.affectedSubjects != null)
|
|
1927
|
+
ctx.affected_subjects = options.affectedSubjects;
|
|
1928
|
+
if (options.remediationStatus)
|
|
1929
|
+
ctx.remediation_status = options.remediationStatus;
|
|
1779
1930
|
payload.ai_context = ctx;
|
|
1780
1931
|
}
|
|
1781
1932
|
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
@@ -1783,34 +1934,37 @@ export class Witness {
|
|
|
1783
1934
|
this.buffer.enqueueMany([payload]);
|
|
1784
1935
|
return payload;
|
|
1785
1936
|
}
|
|
1786
|
-
// ──
|
|
1937
|
+
// ── Performance Metrics (AI-PERF.1) ───────────────────────────
|
|
1787
1938
|
/**
|
|
1788
|
-
* Witness
|
|
1939
|
+
* Witness performance/accuracy metrics (AI-PERF.1).
|
|
1940
|
+
*
|
|
1941
|
+
* Records model performance benchmarks per EU AI Act Art. 15(1)
|
|
1942
|
+
* accuracy requirements and NIST AI RMF MEASURE 2.5.
|
|
1789
1943
|
*/
|
|
1790
|
-
|
|
1944
|
+
witnessPerformance(options) {
|
|
1945
|
+
const fa = options.metricsEvaluated;
|
|
1946
|
+
const fb = options.metricsPassing;
|
|
1947
|
+
const fc = BENCHMARK_TYPE_CODES[options.benchmarkType] ?? 5;
|
|
1791
1948
|
const [ts, epoch] = timestampMs();
|
|
1792
|
-
const
|
|
1793
|
-
const fb = featureCount;
|
|
1794
|
-
const fc = options?.classBalanceRatio != null ? Math.floor(options.classBalanceRatio * 1000) : 0;
|
|
1795
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-DATA.3", fa, fb, fc, ts);
|
|
1949
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-PERF.1", fa, fb, fc, ts);
|
|
1796
1950
|
const payload = {
|
|
1797
|
-
procedure_id: "AI-
|
|
1951
|
+
procedure_id: "AI-PERF.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1798
1952
|
clearing_level: this.config.clearingLevel,
|
|
1799
1953
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1800
1954
|
};
|
|
1801
1955
|
if (this.config.clearingLevel <= 1) {
|
|
1802
|
-
payload.ai_model_id =
|
|
1803
|
-
const ctx = {
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
if (options
|
|
1807
|
-
ctx.
|
|
1808
|
-
if (options
|
|
1809
|
-
ctx.
|
|
1810
|
-
if (options
|
|
1811
|
-
ctx.
|
|
1812
|
-
if (options
|
|
1813
|
-
ctx.
|
|
1956
|
+
payload.ai_model_id = options.modelUnderTest ? `perf-${options.modelUnderTest}` : `perf-${options.benchmarkType}`;
|
|
1957
|
+
const ctx = { provider: "performance-metrics", benchmark_type: options.benchmarkType };
|
|
1958
|
+
if (options.benchmarkId)
|
|
1959
|
+
ctx.benchmark_id = options.benchmarkId;
|
|
1960
|
+
if (options.datasetHash)
|
|
1961
|
+
ctx.dataset_hash = options.datasetHash;
|
|
1962
|
+
if (options.threshold != null)
|
|
1963
|
+
ctx.threshold = options.threshold;
|
|
1964
|
+
if (options.score != null)
|
|
1965
|
+
ctx.score = options.score;
|
|
1966
|
+
if (options.modelUnderTest)
|
|
1967
|
+
ctx.model_under_test = options.modelUnderTest;
|
|
1814
1968
|
payload.ai_context = ctx;
|
|
1815
1969
|
}
|
|
1816
1970
|
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
@@ -1818,32 +1972,35 @@ export class Witness {
|
|
|
1818
1972
|
this.buffer.enqueueMany([payload]);
|
|
1819
1973
|
return payload;
|
|
1820
1974
|
}
|
|
1975
|
+
// ── Robustness Testing (AI-ROBUST.1) ──────────────────────────
|
|
1821
1976
|
/**
|
|
1822
|
-
* Witness
|
|
1977
|
+
* Witness robustness testing (AI-ROBUST.1).
|
|
1978
|
+
*
|
|
1979
|
+
* Records resilience against errors, faults, and inconsistencies
|
|
1980
|
+
* per EU AI Act Art. 15(3) and NIST AI RMF MEASURE 2.6.
|
|
1823
1981
|
*/
|
|
1824
|
-
|
|
1825
|
-
const
|
|
1826
|
-
const
|
|
1982
|
+
witnessRobustness(options) {
|
|
1983
|
+
const fa = options.perturbationsTested;
|
|
1984
|
+
const fb = options.perturbationsSurvived;
|
|
1985
|
+
const fc = PERTURBATION_TYPE_CODES[options.perturbationType] ?? 5;
|
|
1827
1986
|
const [ts, epoch] = timestampMs();
|
|
1828
|
-
const
|
|
1829
|
-
const fb = completed ? 1 : 0;
|
|
1830
|
-
const fc = PII_EVENT_TYPES[eventType] ?? 0;
|
|
1831
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-DATA.4", fa, fb, fc, ts);
|
|
1987
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-ROBUST.1", fa, fb, fc, ts);
|
|
1832
1988
|
const payload = {
|
|
1833
|
-
procedure_id: "AI-
|
|
1989
|
+
procedure_id: "AI-ROBUST.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1834
1990
|
clearing_level: this.config.clearingLevel,
|
|
1835
1991
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1836
1992
|
};
|
|
1837
1993
|
if (this.config.clearingLevel <= 1) {
|
|
1838
|
-
payload.ai_model_id =
|
|
1839
|
-
const ctx = {
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1994
|
+
payload.ai_model_id = `robust-${options.perturbationType}`;
|
|
1995
|
+
const ctx = { provider: "robustness-testing", perturbation_type: options.perturbationType };
|
|
1996
|
+
if (options.testSuiteId)
|
|
1997
|
+
ctx.test_suite_id = options.testSuiteId;
|
|
1998
|
+
if (options.degradationPct != null)
|
|
1999
|
+
ctx.degradation_pct = options.degradationPct;
|
|
2000
|
+
if (options.baselineScore != null)
|
|
2001
|
+
ctx.baseline_score = options.baselineScore;
|
|
2002
|
+
if (options.perturbedScore != null)
|
|
2003
|
+
ctx.perturbed_score = options.perturbedScore;
|
|
1847
2004
|
payload.ai_context = ctx;
|
|
1848
2005
|
}
|
|
1849
2006
|
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
@@ -1851,42 +2008,35 @@ export class Witness {
|
|
|
1851
2008
|
this.buffer.enqueueMany([payload]);
|
|
1852
2009
|
return payload;
|
|
1853
2010
|
}
|
|
1854
|
-
// ──
|
|
2011
|
+
// ── Cybersecurity Attestation (AI-CYBER.1) ────────────────────
|
|
1855
2012
|
/**
|
|
1856
|
-
* Witness
|
|
1857
|
-
* Records that a bias assessment was conducted, how many protected
|
|
1858
|
-
* attributes were tested, and whether all fairness thresholds were met.
|
|
2013
|
+
* Witness cybersecurity measure attestation (AI-CYBER.1).
|
|
1859
2014
|
*
|
|
1860
|
-
*
|
|
1861
|
-
*
|
|
1862
|
-
* @param options.maxDisparityPct - Worst-case disparity percentage (0-100)
|
|
1863
|
-
* @param options.methodology - Assessment methodology name
|
|
1864
|
-
* @param options.protectedAttributes - List of protected attributes tested
|
|
2015
|
+
* Records cybersecurity assessment results per EU AI Act Art. 15(4)
|
|
2016
|
+
* and NIST Cybersecurity Framework.
|
|
1865
2017
|
*/
|
|
1866
|
-
|
|
2018
|
+
witnessCybersecurity(options) {
|
|
2019
|
+
const fa = options.controlsAssessed;
|
|
2020
|
+
const fb = options.controlsCompliant;
|
|
2021
|
+
const fc = CYBER_FRAMEWORK_CODES[options.framework] ?? 4;
|
|
1867
2022
|
const [ts, epoch] = timestampMs();
|
|
1868
|
-
const
|
|
1869
|
-
const fb = allThresholdsMet ? 1 : 0;
|
|
1870
|
-
const fc = options?.maxDisparityPct != null ? Math.round(options.maxDisparityPct) : 0;
|
|
1871
|
-
const fp = mintFingerprint(this.config.tenantId, "AI-FAIR.3", fa, fb, fc, ts);
|
|
2023
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-CYBER.1", fa, fb, fc, ts);
|
|
1872
2024
|
const payload = {
|
|
1873
|
-
procedure_id: "AI-
|
|
2025
|
+
procedure_id: "AI-CYBER.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
1874
2026
|
clearing_level: this.config.clearingLevel,
|
|
1875
2027
|
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
1876
2028
|
};
|
|
1877
2029
|
if (this.config.clearingLevel <= 1) {
|
|
1878
|
-
payload.ai_model_id =
|
|
1879
|
-
const ctx = {
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
if (options
|
|
1885
|
-
ctx.
|
|
1886
|
-
if (options
|
|
1887
|
-
ctx.
|
|
1888
|
-
if (options?.maxDisparityPct != null)
|
|
1889
|
-
ctx.max_disparity_pct = options.maxDisparityPct;
|
|
2030
|
+
payload.ai_model_id = `cyber-${options.framework}`;
|
|
2031
|
+
const ctx = { provider: "cybersecurity-assessment", framework: options.framework };
|
|
2032
|
+
if (options.assessmentId)
|
|
2033
|
+
ctx.assessment_id = options.assessmentId;
|
|
2034
|
+
if (options.frameworkVersion)
|
|
2035
|
+
ctx.framework_version = options.frameworkVersion;
|
|
2036
|
+
if (options.findingsCount != null)
|
|
2037
|
+
ctx.findings_count = options.findingsCount;
|
|
2038
|
+
if (options.criticalFindings != null)
|
|
2039
|
+
ctx.critical_findings = options.criticalFindings;
|
|
1890
2040
|
payload.ai_context = ctx;
|
|
1891
2041
|
}
|
|
1892
2042
|
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
@@ -1894,8 +2044,1136 @@ export class Witness {
|
|
|
1894
2044
|
this.buffer.enqueueMany([payload]);
|
|
1895
2045
|
return payload;
|
|
1896
2046
|
}
|
|
1897
|
-
// ──
|
|
1898
|
-
|
|
2047
|
+
// ── Transparency Disclosure (AI-TRANS.1) ──────────────────────
|
|
2048
|
+
/**
|
|
2049
|
+
* Witness transparency disclosure (AI-TRANS.1).
|
|
2050
|
+
*
|
|
2051
|
+
* Records AI usage disclosures to deployers, end users, or data
|
|
2052
|
+
* subjects per EU AI Act Art. 13 and GDPR Art. 13/14.
|
|
2053
|
+
*/
|
|
2054
|
+
witnessTransparency(options) {
|
|
2055
|
+
const fa = options.disclosuresMade;
|
|
2056
|
+
const fb = DISCLOSURE_TYPE_CODES[options.disclosureType] ?? 0;
|
|
2057
|
+
const fc = RECIPIENT_TYPE_CODES[options.recipientType] ?? 0;
|
|
2058
|
+
const [ts, epoch] = timestampMs();
|
|
2059
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-TRANS.1", fa, fb, fc, ts);
|
|
2060
|
+
const payload = {
|
|
2061
|
+
procedure_id: "AI-TRANS.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2062
|
+
clearing_level: this.config.clearingLevel,
|
|
2063
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2064
|
+
};
|
|
2065
|
+
if (this.config.clearingLevel <= 1) {
|
|
2066
|
+
payload.ai_model_id = `trans-${options.disclosureType}`;
|
|
2067
|
+
const ctx = { provider: "transparency-disclosure", disclosure_type: options.disclosureType, recipient_type: options.recipientType };
|
|
2068
|
+
if (options.disclosureId)
|
|
2069
|
+
ctx.disclosure_id = options.disclosureId;
|
|
2070
|
+
if (options.contentHash)
|
|
2071
|
+
ctx.content_hash = options.contentHash;
|
|
2072
|
+
if (options.channel)
|
|
2073
|
+
ctx.channel = options.channel;
|
|
2074
|
+
payload.ai_context = ctx;
|
|
2075
|
+
}
|
|
2076
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2077
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2078
|
+
this.buffer.enqueueMany([payload]);
|
|
2079
|
+
return payload;
|
|
2080
|
+
}
|
|
2081
|
+
// ── Watermark Verification (AI-WATERMARK.1) ──────────────────
|
|
2082
|
+
/**
|
|
2083
|
+
* Witness watermark verification (AI-WATERMARK.1).
|
|
2084
|
+
*
|
|
2085
|
+
* Records verification that AI content marking survived downstream
|
|
2086
|
+
* processing per EU AI Act Art. 50(2). Distinct from AI-MARK.1
|
|
2087
|
+
* which witnesses marking; this witnesses verification.
|
|
2088
|
+
*/
|
|
2089
|
+
witnessWatermarkVerification(options) {
|
|
2090
|
+
const fa = options.itemsChecked;
|
|
2091
|
+
const fb = options.watermarksDetected;
|
|
2092
|
+
const fc = DETECTION_METHOD_CODES[options.detectionMethod] ?? 4;
|
|
2093
|
+
const [ts, epoch] = timestampMs();
|
|
2094
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-WATERMARK.1", fa, fb, fc, ts);
|
|
2095
|
+
const payload = {
|
|
2096
|
+
procedure_id: "AI-WATERMARK.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2097
|
+
clearing_level: this.config.clearingLevel,
|
|
2098
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2099
|
+
};
|
|
2100
|
+
if (this.config.clearingLevel <= 1) {
|
|
2101
|
+
payload.ai_model_id = `watermark-${options.detectionMethod}`;
|
|
2102
|
+
const ctx = { provider: "watermark-verification", detection_method: options.detectionMethod };
|
|
2103
|
+
if (options.contentHash)
|
|
2104
|
+
ctx.content_hash = options.contentHash;
|
|
2105
|
+
if (options.watermarkProvider)
|
|
2106
|
+
ctx.watermark_provider = options.watermarkProvider;
|
|
2107
|
+
if (options.confidenceScore != null)
|
|
2108
|
+
ctx.confidence_score = options.confidenceScore;
|
|
2109
|
+
if (options.strippedCount != null)
|
|
2110
|
+
ctx.stripped_count = options.strippedCount;
|
|
2111
|
+
payload.ai_context = ctx;
|
|
2112
|
+
}
|
|
2113
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2114
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2115
|
+
this.buffer.enqueueMany([payload]);
|
|
2116
|
+
return payload;
|
|
2117
|
+
}
|
|
2118
|
+
// ── Data Protection Impact Assessment (AI-DPIA.1) ─────────────
|
|
2119
|
+
/**
|
|
2120
|
+
* Witness data protection impact assessment (AI-DPIA.1).
|
|
2121
|
+
*
|
|
2122
|
+
* Records DPIA completion per GDPR Art. 35 and EU AI Act Art. 27
|
|
2123
|
+
* for high-risk AI processing.
|
|
2124
|
+
*/
|
|
2125
|
+
witnessDpia(options) {
|
|
2126
|
+
const fa = options.risksIdentified;
|
|
2127
|
+
const fb = options.risksMitigated;
|
|
2128
|
+
const fc = PROCESSING_TYPE_CODES[options.processingType] ?? 4;
|
|
2129
|
+
const [ts, epoch] = timestampMs();
|
|
2130
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-DPIA.1", fa, fb, fc, ts);
|
|
2131
|
+
const payload = {
|
|
2132
|
+
procedure_id: "AI-DPIA.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2133
|
+
clearing_level: this.config.clearingLevel,
|
|
2134
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2135
|
+
};
|
|
2136
|
+
if (this.config.clearingLevel <= 1) {
|
|
2137
|
+
payload.ai_model_id = `dpia-${options.processingType}`;
|
|
2138
|
+
const ctx = { provider: "impact-assessment", processing_type: options.processingType };
|
|
2139
|
+
if (options.dpiaId)
|
|
2140
|
+
ctx.dpia_id = options.dpiaId;
|
|
2141
|
+
if (options.assessmentDate)
|
|
2142
|
+
ctx.assessment_date = options.assessmentDate;
|
|
2143
|
+
if (options.dpoConsulted != null)
|
|
2144
|
+
ctx.dpo_consulted = options.dpoConsulted;
|
|
2145
|
+
if (options.residualRiskLevel)
|
|
2146
|
+
ctx.residual_risk_level = options.residualRiskLevel;
|
|
2147
|
+
if (options.supervisoryAuthorityConsulted != null)
|
|
2148
|
+
ctx.supervisory_authority_consulted = options.supervisoryAuthorityConsulted;
|
|
2149
|
+
payload.ai_context = ctx;
|
|
2150
|
+
}
|
|
2151
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2152
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2153
|
+
this.buffer.enqueueMany([payload]);
|
|
2154
|
+
return payload;
|
|
2155
|
+
}
|
|
2156
|
+
// ── Automated Decision Notification (AI-AUTO.1) ───────────────
|
|
2157
|
+
/**
|
|
2158
|
+
* Witness automated decision notification (AI-AUTO.1).
|
|
2159
|
+
*
|
|
2160
|
+
* Records notification of automated decisions with legal effects
|
|
2161
|
+
* per GDPR Art. 22 and EU AI Act Art. 14.
|
|
2162
|
+
*/
|
|
2163
|
+
witnessAutomatedDecision(options) {
|
|
2164
|
+
const fa = options.decisionsMade;
|
|
2165
|
+
const fb = options.humanReviewed;
|
|
2166
|
+
const fc = DECISION_TYPE_CODES[options.decisionType] ?? 5;
|
|
2167
|
+
const [ts, epoch] = timestampMs();
|
|
2168
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-AUTO.1", fa, fb, fc, ts);
|
|
2169
|
+
const payload = {
|
|
2170
|
+
procedure_id: "AI-AUTO.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2171
|
+
clearing_level: this.config.clearingLevel,
|
|
2172
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2173
|
+
};
|
|
2174
|
+
if (this.config.clearingLevel <= 1) {
|
|
2175
|
+
payload.ai_model_id = `auto-${options.decisionType}`;
|
|
2176
|
+
const ctx = { provider: "automated-decision", decision_type: options.decisionType };
|
|
2177
|
+
if (options.decisionId)
|
|
2178
|
+
ctx.decision_id = options.decisionId;
|
|
2179
|
+
if (options.subjectNotified != null)
|
|
2180
|
+
ctx.subject_notified = options.subjectNotified;
|
|
2181
|
+
if (options.optOutAvailable != null)
|
|
2182
|
+
ctx.opt_out_available = options.optOutAvailable;
|
|
2183
|
+
if (options.humanReviewRequested != null)
|
|
2184
|
+
ctx.human_review_requested = options.humanReviewRequested;
|
|
2185
|
+
payload.ai_context = ctx;
|
|
2186
|
+
}
|
|
2187
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2188
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2189
|
+
this.buffer.enqueueMany([payload]);
|
|
2190
|
+
return payload;
|
|
2191
|
+
}
|
|
2192
|
+
// ── Autonomous Generation Depth (AI-AUTO.2) ──────────────────
|
|
2193
|
+
/**
|
|
2194
|
+
* Witness autonomous generation depth (AI-AUTO.2).
|
|
2195
|
+
*
|
|
2196
|
+
* Records the depth of AI-to-AI generation cycles and whether
|
|
2197
|
+
* a human approval gate was present. EU AI Act Art. 14, EO 14110 Sec. 3.
|
|
2198
|
+
*/
|
|
2199
|
+
witnessGenerationDepth(options) {
|
|
2200
|
+
const fa = options.maxDepth;
|
|
2201
|
+
const fb = options.observedDepth;
|
|
2202
|
+
const fc = options.humanGatePresent ? 1 : 0;
|
|
2203
|
+
const [ts, epoch] = timestampMs();
|
|
2204
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-AUTO.2", fa, fb, fc, ts);
|
|
2205
|
+
const payload = {
|
|
2206
|
+
procedure_id: "AI-AUTO.2", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2207
|
+
clearing_level: this.config.clearingLevel,
|
|
2208
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2209
|
+
};
|
|
2210
|
+
if (this.config.clearingLevel <= 1) {
|
|
2211
|
+
payload.ai_model_id = options.sourceAgentId || "autonomous-generator";
|
|
2212
|
+
const ctx = { provider: "generation-depth", max_depth: fa, observed_depth: fb };
|
|
2213
|
+
if (options.generationContext)
|
|
2214
|
+
ctx.generation_context = options.generationContext;
|
|
2215
|
+
if (options.sourceAgentId)
|
|
2216
|
+
ctx.source_agent_id = options.sourceAgentId;
|
|
2217
|
+
if (options.mergeTarget)
|
|
2218
|
+
ctx.merge_target = options.mergeTarget;
|
|
2219
|
+
payload.ai_context = ctx;
|
|
2220
|
+
}
|
|
2221
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2222
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2223
|
+
this.buffer.enqueueMany([payload]);
|
|
2224
|
+
return payload;
|
|
2225
|
+
}
|
|
2226
|
+
// ── External Timestamp Attestation (AI-AUDIT.2) ──────────────
|
|
2227
|
+
/**
|
|
2228
|
+
* Witness external timestamp attestation (AI-AUDIT.2).
|
|
2229
|
+
*
|
|
2230
|
+
* Records that a batch of anchors was anchored to an independent
|
|
2231
|
+
* RFC 3161 Timestamp Authority. NIST 800-53 AU-10 (Non-repudiation).
|
|
2232
|
+
*/
|
|
2233
|
+
witnessTimestampAttestation(options) {
|
|
2234
|
+
const fa = options.anchorCount;
|
|
2235
|
+
const fb = options.tsaVerified ? 1 : 0;
|
|
2236
|
+
const TSA_CODES = { none: 0, freetsa: 1, digicert: 2, sectigo: 3, custom: 4 };
|
|
2237
|
+
const fc = TSA_CODES[options.tsaProvider] ?? 4;
|
|
2238
|
+
const [ts, epoch] = timestampMs();
|
|
2239
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-AUDIT.2", fa, fb, fc, ts);
|
|
2240
|
+
const payload = {
|
|
2241
|
+
procedure_id: "AI-AUDIT.2", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2242
|
+
clearing_level: this.config.clearingLevel,
|
|
2243
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2244
|
+
};
|
|
2245
|
+
if (this.config.clearingLevel <= 1) {
|
|
2246
|
+
payload.ai_model_id = "merkle-rollup";
|
|
2247
|
+
const ctx = { provider: "timestamp-attestation", tsa_provider: options.tsaProvider };
|
|
2248
|
+
if (options.merkleRoot)
|
|
2249
|
+
ctx.merkle_root = options.merkleRoot;
|
|
2250
|
+
if (options.tsaUrl)
|
|
2251
|
+
ctx.tsa_url = options.tsaUrl;
|
|
2252
|
+
if (options.tsaSerial)
|
|
2253
|
+
ctx.tsa_serial = options.tsaSerial;
|
|
2254
|
+
payload.ai_context = ctx;
|
|
2255
|
+
}
|
|
2256
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2257
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2258
|
+
this.buffer.enqueueMany([payload]);
|
|
2259
|
+
return payload;
|
|
2260
|
+
}
|
|
2261
|
+
// ── Dual-Use Model Classification (AI-DUALUSE.1) ─────────────
|
|
2262
|
+
/**
|
|
2263
|
+
* Witness dual-use model classification (AI-DUALUSE.1).
|
|
2264
|
+
*
|
|
2265
|
+
* Records classification and reporting of dual-use foundation
|
|
2266
|
+
* models per EO 14110 Sec 4(a) and NIST AI RMF GOVERN 1.1.
|
|
2267
|
+
*/
|
|
2268
|
+
witnessDualUse(options) {
|
|
2269
|
+
const fa = options.classificationCode;
|
|
2270
|
+
const fb = REPORTING_STATUS_CODES[options.reportingStatus] ?? 0;
|
|
2271
|
+
const fc = options.daysSinceClassification;
|
|
2272
|
+
const [ts, epoch] = timestampMs();
|
|
2273
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-DUALUSE.1", fa, fb, fc, ts);
|
|
2274
|
+
const payload = {
|
|
2275
|
+
procedure_id: "AI-DUALUSE.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2276
|
+
clearing_level: this.config.clearingLevel,
|
|
2277
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2278
|
+
};
|
|
2279
|
+
if (this.config.clearingLevel <= 1) {
|
|
2280
|
+
payload.ai_model_id = options.modelId ?? `dualuse-class-${options.classificationCode}`;
|
|
2281
|
+
const ctx = { provider: "dual-use-classification", reporting_status: options.reportingStatus };
|
|
2282
|
+
if (options.modelId)
|
|
2283
|
+
ctx.model_id = options.modelId;
|
|
2284
|
+
if (options.classificationBasis)
|
|
2285
|
+
ctx.classification_basis = options.classificationBasis;
|
|
2286
|
+
if (options.computeThreshold)
|
|
2287
|
+
ctx.compute_threshold = options.computeThreshold;
|
|
2288
|
+
if (options.authorityNotified)
|
|
2289
|
+
ctx.authority_notified = options.authorityNotified;
|
|
2290
|
+
payload.ai_context = ctx;
|
|
2291
|
+
}
|
|
2292
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2293
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2294
|
+
this.buffer.enqueueMany([payload]);
|
|
2295
|
+
return payload;
|
|
2296
|
+
}
|
|
2297
|
+
// ── Supply Chain Risk (AI-SUPPLY.1) ───────────────────────────
|
|
2298
|
+
/**
|
|
2299
|
+
* Witness supply chain risk assessment (AI-SUPPLY.1).
|
|
2300
|
+
*
|
|
2301
|
+
* Records third-party AI supply chain risk metrics per NIST AI
|
|
2302
|
+
* RMF MEASURE 3.1, G7/CISA SBOM-AI, and EO 14028.
|
|
2303
|
+
*/
|
|
2304
|
+
witnessSupplyChainRisk(options) {
|
|
2305
|
+
const fa = options.suppliersAssessed;
|
|
2306
|
+
const fb = options.suppliersCompliant;
|
|
2307
|
+
const fc = SUPPLY_RISK_CODES[options.riskLevel] ?? 0;
|
|
2308
|
+
const [ts, epoch] = timestampMs();
|
|
2309
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-SUPPLY.1", fa, fb, fc, ts);
|
|
2310
|
+
const payload = {
|
|
2311
|
+
procedure_id: "AI-SUPPLY.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2312
|
+
clearing_level: this.config.clearingLevel,
|
|
2313
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2314
|
+
};
|
|
2315
|
+
if (this.config.clearingLevel <= 1) {
|
|
2316
|
+
payload.ai_model_id = `supply-risk-${options.riskLevel}`;
|
|
2317
|
+
const ctx = { provider: "supply-chain-risk", risk_level: options.riskLevel };
|
|
2318
|
+
if (options.supplierIdHash)
|
|
2319
|
+
ctx.supplier_id_hash = options.supplierIdHash;
|
|
2320
|
+
if (options.vulnerabilityCount != null)
|
|
2321
|
+
ctx.vulnerability_count = options.vulnerabilityCount;
|
|
2322
|
+
if (options.lastAuditDate)
|
|
2323
|
+
ctx.last_audit_date = options.lastAuditDate;
|
|
2324
|
+
if (options.updateCadenceDays != null)
|
|
2325
|
+
ctx.update_cadence_days = options.updateCadenceDays;
|
|
2326
|
+
payload.ai_context = ctx;
|
|
2327
|
+
}
|
|
2328
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2329
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2330
|
+
this.buffer.enqueueMany([payload]);
|
|
2331
|
+
return payload;
|
|
2332
|
+
}
|
|
2333
|
+
// ── Post-Market Monitoring (AI-PMM.1) ─────────────────────────
|
|
2334
|
+
/**
|
|
2335
|
+
* Witness post-market monitoring attestation (AI-PMM.1).
|
|
2336
|
+
*
|
|
2337
|
+
* Records execution of post-market monitoring plans per EU AI Act
|
|
2338
|
+
* Art. 72 and NIST AI RMF MANAGE 4.1.
|
|
2339
|
+
*/
|
|
2340
|
+
witnessPostMarketMonitoring(options) {
|
|
2341
|
+
const fa = options.monitoringChecksRun;
|
|
2342
|
+
const fb = options.anomaliesDetected;
|
|
2343
|
+
const fc = PMM_TYPE_CODES[options.monitoringType] ?? 4;
|
|
2344
|
+
const [ts, epoch] = timestampMs();
|
|
2345
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-PMM.1", fa, fb, fc, ts);
|
|
2346
|
+
const payload = {
|
|
2347
|
+
procedure_id: "AI-PMM.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2348
|
+
clearing_level: this.config.clearingLevel,
|
|
2349
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2350
|
+
};
|
|
2351
|
+
if (this.config.clearingLevel <= 1) {
|
|
2352
|
+
payload.ai_model_id = `pmm-${options.monitoringType}`;
|
|
2353
|
+
const ctx = { provider: "post-market-monitoring", monitoring_type: options.monitoringType };
|
|
2354
|
+
if (options.monitoringPlanHash)
|
|
2355
|
+
ctx.monitoring_plan_hash = options.monitoringPlanHash;
|
|
2356
|
+
if (options.periodStart)
|
|
2357
|
+
ctx.period_start = options.periodStart;
|
|
2358
|
+
if (options.periodEnd)
|
|
2359
|
+
ctx.period_end = options.periodEnd;
|
|
2360
|
+
if (options.reportGenerated != null)
|
|
2361
|
+
ctx.report_generated = options.reportGenerated;
|
|
2362
|
+
payload.ai_context = ctx;
|
|
2363
|
+
}
|
|
2364
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2365
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2366
|
+
this.buffer.enqueueMany([payload]);
|
|
2367
|
+
return payload;
|
|
2368
|
+
}
|
|
2369
|
+
// ── Chain, Violation, Charter, Registry, Reviewer, Safe State ───
|
|
2370
|
+
/**
|
|
2371
|
+
* Witness a multi-agent chain handoff (AI-CHAIN.1).
|
|
2372
|
+
*/
|
|
2373
|
+
witnessChainHandoff(depth, targetAgent, options) {
|
|
2374
|
+
const accepted = options?.accepted ?? true;
|
|
2375
|
+
const [ts, epoch] = timestampMs();
|
|
2376
|
+
const fa = depth;
|
|
2377
|
+
const fb = this.config.cycleId ? 1 : 0;
|
|
2378
|
+
const fc = accepted ? 1 : 0;
|
|
2379
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-CHAIN.1", fa, fb, fc, ts);
|
|
2380
|
+
const payload = {
|
|
2381
|
+
procedure_id: "AI-CHAIN.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2382
|
+
clearing_level: this.config.clearingLevel,
|
|
2383
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2384
|
+
};
|
|
2385
|
+
if (this.config.clearingLevel <= 1) {
|
|
2386
|
+
payload.ai_model_id = targetAgent;
|
|
2387
|
+
payload.ai_context = {
|
|
2388
|
+
provider: "chain", target_agent: targetAgent, depth, accepted,
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2392
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2393
|
+
this.buffer.enqueueMany([payload]);
|
|
2394
|
+
return payload;
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Witness a trust-level-aware chain handoff (AI-CHAIN.1 + optional AI-CHAIN.2).
|
|
2398
|
+
*
|
|
2399
|
+
* Tracks the effective trust level across all agents in the chain.
|
|
2400
|
+
* If chainMinTrustLevel is configured and strict mode is active,
|
|
2401
|
+
* throws ChainTrustError when the effective level drops below the minimum.
|
|
2402
|
+
*
|
|
2403
|
+
* Auto-mints AI-CHAIN.2 (trust degradation) when the effective trust
|
|
2404
|
+
* level drops compared to the previous handoff.
|
|
2405
|
+
*/
|
|
2406
|
+
witnessChainTrustHandoff(targetAgentId, targetTrustLevel, options) {
|
|
2407
|
+
const accepted = options?.accepted ?? true;
|
|
2408
|
+
const prevEffective = this._chainTrustLevels.length > 0
|
|
2409
|
+
? Math.min(...this._chainTrustLevels)
|
|
2410
|
+
: targetTrustLevel;
|
|
2411
|
+
this._chainTrustLevels.push(targetTrustLevel);
|
|
2412
|
+
const effectiveTrustLevel = Math.min(...this._chainTrustLevels);
|
|
2413
|
+
// Enforce minimum if configured
|
|
2414
|
+
const minLevel = this.config.chainMinTrustLevel;
|
|
2415
|
+
if (minLevel !== undefined && effectiveTrustLevel < minLevel && this._strict) {
|
|
2416
|
+
throw new ChainTrustError(effectiveTrustLevel, minLevel);
|
|
2417
|
+
}
|
|
2418
|
+
const [ts, epoch] = timestampMs();
|
|
2419
|
+
const fa = this._chainTrustLevels.length;
|
|
2420
|
+
const fb = targetTrustLevel;
|
|
2421
|
+
const fc = effectiveTrustLevel;
|
|
2422
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-CHAIN.1", fa, fb, fc, ts);
|
|
2423
|
+
const payload = {
|
|
2424
|
+
procedure_id: "AI-CHAIN.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2425
|
+
clearing_level: this.config.clearingLevel,
|
|
2426
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2427
|
+
};
|
|
2428
|
+
if (this.config.clearingLevel <= 1) {
|
|
2429
|
+
payload.ai_model_id = targetAgentId;
|
|
2430
|
+
payload.ai_context = {
|
|
2431
|
+
provider: "chain-trust",
|
|
2432
|
+
target_agent: targetAgentId,
|
|
2433
|
+
target_trust_level: targetTrustLevel,
|
|
2434
|
+
effective_trust_level: effectiveTrustLevel,
|
|
2435
|
+
chain_depth: this._chainTrustLevels.length,
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
const cycleId = options?.cycleId ?? this.config.cycleId;
|
|
2439
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2440
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2441
|
+
if (cycleId)
|
|
2442
|
+
payload.cycle_id = cycleId;
|
|
2443
|
+
const payloads = [payload];
|
|
2444
|
+
// Auto-mint AI-CHAIN.2 if trust degraded
|
|
2445
|
+
if (this._chainTrustLevels.length > 1 && effectiveTrustLevel < prevEffective) {
|
|
2446
|
+
const degradation = extractChainTrustDegradationPayload(this.config.tenantId, prevEffective, effectiveTrustLevel, this.config.clearingLevel, this.config.agentId, this.config.signingKey, this.config.signingKeyId, this.config.signingKeyVersion, cycleId, policyHash, this.config.signingAlgorithm);
|
|
2447
|
+
if (this.config.clearingLevel <= 1) {
|
|
2448
|
+
degradation.ai_context = {
|
|
2449
|
+
provider: "chain-trust-degradation",
|
|
2450
|
+
previous_effective: prevEffective,
|
|
2451
|
+
new_effective: effectiveTrustLevel,
|
|
2452
|
+
target_agent: targetAgentId,
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
payloads.push(degradation);
|
|
2456
|
+
}
|
|
2457
|
+
this.buffer.enqueueMany(payloads);
|
|
2458
|
+
return payload;
|
|
2459
|
+
}
|
|
2460
|
+
/** The effective (minimum) trust level across all chain handoffs. Returns 4 if no handoffs yet. */
|
|
2461
|
+
get chainEffectiveTrustLevel() {
|
|
2462
|
+
return this._chainTrustLevels.length === 0 ? 4 : Math.min(...this._chainTrustLevels);
|
|
2463
|
+
}
|
|
2464
|
+
/** The trust levels recorded at each chain handoff. */
|
|
2465
|
+
get chainTrustLevels() {
|
|
2466
|
+
return this._chainTrustLevels;
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Witness a policy violation (AI-VIO.1).
|
|
2470
|
+
*/
|
|
2471
|
+
witnessViolation(severity, description, options) {
|
|
2472
|
+
const autoDetected = options?.autoDetected ?? false;
|
|
2473
|
+
const policyCategory = options?.policyCategory ?? "unspecified";
|
|
2474
|
+
const [ts, epoch] = timestampMs();
|
|
2475
|
+
const fa = Math.max(1, Math.min(4, severity));
|
|
2476
|
+
const fb = autoDetected ? 1 : 0;
|
|
2477
|
+
const fc = POLICY_CATEGORIES[policyCategory] ?? 0;
|
|
2478
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-VIO.1", fa, fb, fc, ts);
|
|
2479
|
+
const payload = {
|
|
2480
|
+
procedure_id: "AI-VIO.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2481
|
+
clearing_level: this.config.clearingLevel,
|
|
2482
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2483
|
+
};
|
|
2484
|
+
if (this.config.clearingLevel <= 1) {
|
|
2485
|
+
payload.ai_model_id = `violation-sev${fa}`;
|
|
2486
|
+
payload.ai_context = {
|
|
2487
|
+
provider: "violation", severity: fa, description, auto_detected: autoDetected, policy_category: policyCategory,
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2491
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2492
|
+
this.buffer.enqueueMany([payload]);
|
|
2493
|
+
return payload;
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Witness an agent charter or system prompt hash (AI-CHR.1).
|
|
2497
|
+
*/
|
|
2498
|
+
witnessCharter(options) {
|
|
2499
|
+
if (!options.charterText && !options.charterHash) {
|
|
2500
|
+
throw new Error("Provide charterText or charterHash");
|
|
2501
|
+
}
|
|
2502
|
+
const computed = options.charterHash ?? sha256Truncated(options.charterText);
|
|
2503
|
+
const match = options.expectedHash ? computed === options.expectedHash : true;
|
|
2504
|
+
const [ts, epoch] = timestampMs();
|
|
2505
|
+
const fa = 1, fb = match ? 1 : 0, fc = 0;
|
|
2506
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-CHR.1", fa, fb, fc, ts);
|
|
2507
|
+
const payload = {
|
|
2508
|
+
procedure_id: "AI-CHR.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2509
|
+
clearing_level: this.config.clearingLevel,
|
|
2510
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2511
|
+
};
|
|
2512
|
+
if (this.config.clearingLevel <= 1) {
|
|
2513
|
+
payload.ai_model_id = "charter";
|
|
2514
|
+
const ctx = { provider: "charter", charter_hash: computed };
|
|
2515
|
+
if (options.expectedHash)
|
|
2516
|
+
ctx.expected_hash = options.expectedHash;
|
|
2517
|
+
payload.ai_context = ctx;
|
|
2518
|
+
}
|
|
2519
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2520
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2521
|
+
this.buffer.enqueueMany([payload]);
|
|
2522
|
+
return payload;
|
|
2523
|
+
}
|
|
2524
|
+
/**
|
|
2525
|
+
* Witness a model registry check (AI-MDL.8).
|
|
2526
|
+
*/
|
|
2527
|
+
witnessModelRegistry(modelId, registryId, options) {
|
|
2528
|
+
const found = options?.found ?? true;
|
|
2529
|
+
const status = options?.status ?? "approved";
|
|
2530
|
+
const [ts, epoch] = timestampMs();
|
|
2531
|
+
const fa = 1, fb = found ? 1 : 0, fc = APPROVAL_STATUS[status] ?? 0;
|
|
2532
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-MDL.8", fa, fb, fc, ts);
|
|
2533
|
+
const payload = {
|
|
2534
|
+
procedure_id: "AI-MDL.8", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2535
|
+
clearing_level: this.config.clearingLevel,
|
|
2536
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2537
|
+
};
|
|
2538
|
+
if (this.config.clearingLevel <= 1) {
|
|
2539
|
+
payload.ai_model_id = modelId;
|
|
2540
|
+
payload.ai_context = {
|
|
2541
|
+
provider: "model-registry", model_id: modelId, registry_id: registryId, found, status,
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2545
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2546
|
+
this.buffer.enqueueMany([payload]);
|
|
2547
|
+
return payload;
|
|
2548
|
+
}
|
|
2549
|
+
/**
|
|
2550
|
+
* Witness reviewer identity binding (AI-HITL.3).
|
|
2551
|
+
*/
|
|
2552
|
+
witnessReviewerIdentity(required, actual, options) {
|
|
2553
|
+
const method = options?.method ?? "session";
|
|
2554
|
+
const [ts, epoch] = timestampMs();
|
|
2555
|
+
const fa = required, fb = actual, fc = BINDING_METHODS[method] ?? 0;
|
|
2556
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-HITL.3", fa, fb, fc, ts);
|
|
2557
|
+
const payload = {
|
|
2558
|
+
procedure_id: "AI-HITL.3", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2559
|
+
clearing_level: this.config.clearingLevel,
|
|
2560
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2561
|
+
};
|
|
2562
|
+
if (this.config.clearingLevel <= 1) {
|
|
2563
|
+
payload.ai_model_id = "reviewer-identity";
|
|
2564
|
+
const ctx = {
|
|
2565
|
+
provider: "reviewer", required, actual, method,
|
|
2566
|
+
};
|
|
2567
|
+
if (options?.reviewerIdHash)
|
|
2568
|
+
ctx.reviewer_id_hash = options.reviewerIdHash;
|
|
2569
|
+
payload.ai_context = ctx;
|
|
2570
|
+
}
|
|
2571
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2572
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2573
|
+
this.buffer.enqueueMany([payload]);
|
|
2574
|
+
return payload;
|
|
2575
|
+
}
|
|
2576
|
+
/**
|
|
2577
|
+
* Witness safe state attestation (AI-SAFE.1).
|
|
2578
|
+
*/
|
|
2579
|
+
witnessSafeState(options) {
|
|
2580
|
+
const mechanismExists = options?.mechanismExists ?? true;
|
|
2581
|
+
const safeStateConfirmed = options?.safeStateConfirmed ?? true;
|
|
2582
|
+
const [ts, epoch] = timestampMs();
|
|
2583
|
+
const fa = 1, fb = mechanismExists ? 1 : 0, fc = safeStateConfirmed ? 1 : 0;
|
|
2584
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-SAFE.1", fa, fb, fc, ts);
|
|
2585
|
+
const payload = {
|
|
2586
|
+
procedure_id: "AI-SAFE.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2587
|
+
clearing_level: this.config.clearingLevel,
|
|
2588
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2589
|
+
};
|
|
2590
|
+
if (this.config.clearingLevel <= 1) {
|
|
2591
|
+
payload.ai_model_id = "safe-state";
|
|
2592
|
+
const ctx = {
|
|
2593
|
+
provider: "safe-state", mechanism_exists: mechanismExists, safe_state_confirmed: safeStateConfirmed,
|
|
2594
|
+
};
|
|
2595
|
+
if (options?.mechanismType)
|
|
2596
|
+
ctx.mechanism_type = options.mechanismType;
|
|
2597
|
+
payload.ai_context = ctx;
|
|
2598
|
+
}
|
|
2599
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2600
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2601
|
+
this.buffer.enqueueMany([payload]);
|
|
2602
|
+
return payload;
|
|
2603
|
+
}
|
|
2604
|
+
// ── Training Data (AI-DATA.3 / AI-DATA.4) ──────────────────────
|
|
2605
|
+
/**
|
|
2606
|
+
* Witness training dataset summary statistics (AI-DATA.3).
|
|
2607
|
+
*/
|
|
2608
|
+
witnessTrainingStats(rowCount, featureCount, options) {
|
|
2609
|
+
const [ts, epoch] = timestampMs();
|
|
2610
|
+
const fa = rowCount;
|
|
2611
|
+
const fb = featureCount;
|
|
2612
|
+
const fc = options?.classBalanceRatio != null ? Math.floor(options.classBalanceRatio * 1000) : 0;
|
|
2613
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-DATA.3", fa, fb, fc, ts);
|
|
2614
|
+
const payload = {
|
|
2615
|
+
procedure_id: "AI-DATA.3", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2616
|
+
clearing_level: this.config.clearingLevel,
|
|
2617
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2618
|
+
};
|
|
2619
|
+
if (this.config.clearingLevel <= 1) {
|
|
2620
|
+
payload.ai_model_id = "training-stats";
|
|
2621
|
+
const ctx = {
|
|
2622
|
+
provider: "training-stats", row_count: rowCount, feature_count: featureCount,
|
|
2623
|
+
};
|
|
2624
|
+
if (options?.classBalanceRatio != null)
|
|
2625
|
+
ctx.class_balance_ratio = options.classBalanceRatio;
|
|
2626
|
+
if (options?.distributionHash)
|
|
2627
|
+
ctx.distribution_hash = options.distributionHash;
|
|
2628
|
+
if (options?.classLabels)
|
|
2629
|
+
ctx.class_labels = options.classLabels;
|
|
2630
|
+
if (options?.summary)
|
|
2631
|
+
ctx.summary = options.summary;
|
|
2632
|
+
payload.ai_context = ctx;
|
|
2633
|
+
}
|
|
2634
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2635
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2636
|
+
this.buffer.enqueueMany([payload]);
|
|
2637
|
+
return payload;
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* Witness a training data PII lifecycle event (AI-DATA.4).
|
|
2641
|
+
*/
|
|
2642
|
+
witnessTrainingPiiLifecycle(recordsAffected, options) {
|
|
2643
|
+
const completed = options?.completed ?? true;
|
|
2644
|
+
const eventType = options?.eventType ?? "unspecified";
|
|
2645
|
+
const [ts, epoch] = timestampMs();
|
|
2646
|
+
const fa = recordsAffected;
|
|
2647
|
+
const fb = completed ? 1 : 0;
|
|
2648
|
+
const fc = PII_EVENT_TYPES[eventType] ?? 0;
|
|
2649
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-DATA.4", fa, fb, fc, ts);
|
|
2650
|
+
const payload = {
|
|
2651
|
+
procedure_id: "AI-DATA.4", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2652
|
+
clearing_level: this.config.clearingLevel,
|
|
2653
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2654
|
+
};
|
|
2655
|
+
if (this.config.clearingLevel <= 1) {
|
|
2656
|
+
payload.ai_model_id = "pii-lifecycle";
|
|
2657
|
+
const ctx = {
|
|
2658
|
+
provider: "pii-lifecycle", event_type: eventType,
|
|
2659
|
+
records_affected: recordsAffected, completed,
|
|
2660
|
+
};
|
|
2661
|
+
if (options?.datasetId)
|
|
2662
|
+
ctx.dataset_id = options.datasetId;
|
|
2663
|
+
if (options?.scope)
|
|
2664
|
+
ctx.scope = options.scope;
|
|
2665
|
+
payload.ai_context = ctx;
|
|
2666
|
+
}
|
|
2667
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2668
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2669
|
+
this.buffer.enqueueMany([payload]);
|
|
2670
|
+
return payload;
|
|
2671
|
+
}
|
|
2672
|
+
// ── Bias Assessment (AI-FAIR.3) ─────────────────────────────────
|
|
2673
|
+
/**
|
|
2674
|
+
* Witness a bias assessment (AI-FAIR.3).
|
|
2675
|
+
* Records that a bias assessment was conducted, how many protected
|
|
2676
|
+
* attributes were tested, and whether all fairness thresholds were met.
|
|
2677
|
+
*
|
|
2678
|
+
* @param protectedAttributeCount - Number of demographic dimensions tested
|
|
2679
|
+
* @param allThresholdsMet - true if all fairness thresholds passed
|
|
2680
|
+
* @param options.maxDisparityPct - Worst-case disparity percentage (0-100)
|
|
2681
|
+
* @param options.methodology - Assessment methodology name
|
|
2682
|
+
* @param options.protectedAttributes - List of protected attributes tested
|
|
2683
|
+
*/
|
|
2684
|
+
witnessBiasAssessment(protectedAttributeCount, allThresholdsMet, options) {
|
|
2685
|
+
const [ts, epoch] = timestampMs();
|
|
2686
|
+
const fa = protectedAttributeCount;
|
|
2687
|
+
const fb = allThresholdsMet ? 1 : 0;
|
|
2688
|
+
const fc = options?.maxDisparityPct != null ? Math.round(options.maxDisparityPct) : 0;
|
|
2689
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-FAIR.3", fa, fb, fc, ts);
|
|
2690
|
+
const payload = {
|
|
2691
|
+
procedure_id: "AI-FAIR.3", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2692
|
+
clearing_level: this.config.clearingLevel,
|
|
2693
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2694
|
+
};
|
|
2695
|
+
if (this.config.clearingLevel <= 1) {
|
|
2696
|
+
payload.ai_model_id = "bias-assessment";
|
|
2697
|
+
const ctx = {
|
|
2698
|
+
provider: "bias-assessment",
|
|
2699
|
+
protected_attribute_count: protectedAttributeCount,
|
|
2700
|
+
all_thresholds_met: allThresholdsMet,
|
|
2701
|
+
};
|
|
2702
|
+
if (options?.methodology)
|
|
2703
|
+
ctx.methodology = options.methodology;
|
|
2704
|
+
if (options?.protectedAttributes)
|
|
2705
|
+
ctx.protected_attributes = options.protectedAttributes;
|
|
2706
|
+
if (options?.maxDisparityPct != null)
|
|
2707
|
+
ctx.max_disparity_pct = options.maxDisparityPct;
|
|
2708
|
+
payload.ai_context = ctx;
|
|
2709
|
+
}
|
|
2710
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2711
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2712
|
+
this.buffer.enqueueMany([payload]);
|
|
2713
|
+
return payload;
|
|
2714
|
+
}
|
|
2715
|
+
// ── Governance Infrastructure Attestation (AI-METAGOV.1) ──────────────
|
|
2716
|
+
/**
|
|
2717
|
+
* Witness governance infrastructure configuration (AI-METAGOV.1).
|
|
2718
|
+
*
|
|
2719
|
+
* Attests the governance system's own configuration using the same
|
|
2720
|
+
* protocol it enforces, per the Recursive Governance architecture.
|
|
2721
|
+
* The governance config must be attested before operational events
|
|
2722
|
+
* can be processed.
|
|
2723
|
+
*/
|
|
2724
|
+
witnessGovernanceConfig(options) {
|
|
2725
|
+
// Canonical serialization: sort rules by id, concat id+expression+version, hash with domain separator
|
|
2726
|
+
const sorted = [...options.rules].sort((a, b) => a.id.localeCompare(b.id));
|
|
2727
|
+
const canonical = sorted.map(r => `${r.id}:${r.expression}:${r.version ?? ""}`).join("|");
|
|
2728
|
+
const configHash = sha256Truncated(`SWT3:GOVERNANCE:${canonical}`, 12);
|
|
2729
|
+
const fa = options.rules.length;
|
|
2730
|
+
const fb = parseInt(configHash.slice(0, 8), 16) % 1000000;
|
|
2731
|
+
const fc = options.governanceVersion;
|
|
2732
|
+
const [ts, epoch] = timestampMs();
|
|
2733
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.1", fa, fb, fc, ts);
|
|
2734
|
+
const payload = {
|
|
2735
|
+
procedure_id: "AI-METAGOV.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2736
|
+
clearing_level: this.config.clearingLevel,
|
|
2737
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2738
|
+
};
|
|
2739
|
+
if (this.config.clearingLevel <= 1) {
|
|
2740
|
+
payload.ai_model_id = `governance-v${options.governanceVersion}`;
|
|
2741
|
+
const ctx = {
|
|
2742
|
+
provider: "governance-infrastructure",
|
|
2743
|
+
config_hash: configHash,
|
|
2744
|
+
rule_count: options.rules.length,
|
|
2745
|
+
governance_version: options.governanceVersion,
|
|
2746
|
+
};
|
|
2747
|
+
if (options.operatorId)
|
|
2748
|
+
ctx.operator_id = options.operatorId;
|
|
2749
|
+
payload.ai_context = ctx;
|
|
2750
|
+
}
|
|
2751
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2752
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2753
|
+
this.buffer.enqueueMany([payload]);
|
|
2754
|
+
return payload;
|
|
2755
|
+
}
|
|
2756
|
+
// ── Policy Downgrade Detection (AI-METAGOV.3) ──────────────────────
|
|
2757
|
+
/**
|
|
2758
|
+
* Check policy version and witness downgrade if detected (AI-METAGOV.3).
|
|
2759
|
+
*
|
|
2760
|
+
* Enforces monotonic policy version progression. If the provided version
|
|
2761
|
+
* is lower than the last known good version, mints a downgrade alert anchor.
|
|
2762
|
+
* Returns the payload if a downgrade was detected, or null if version is normal.
|
|
2763
|
+
*/
|
|
2764
|
+
checkPolicyDowngrade(options) {
|
|
2765
|
+
const isDowngrade = options.policyVersion < this._lastKnownGoodVersion;
|
|
2766
|
+
if (!isDowngrade) {
|
|
2767
|
+
this._lastKnownGoodVersion = Math.max(this._lastKnownGoodVersion, options.policyVersion);
|
|
2768
|
+
return null;
|
|
2769
|
+
}
|
|
2770
|
+
const fa = options.policyVersion;
|
|
2771
|
+
const fb = safeHexInt(options.policyContentHash);
|
|
2772
|
+
const fc = 1; // downgrade detected
|
|
2773
|
+
const [ts, epoch] = timestampMs();
|
|
2774
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.3", fa, fb, fc, ts);
|
|
2775
|
+
const payload = {
|
|
2776
|
+
procedure_id: "AI-METAGOV.3", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2777
|
+
clearing_level: this.config.clearingLevel,
|
|
2778
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2779
|
+
};
|
|
2780
|
+
if (this.config.clearingLevel <= 1) {
|
|
2781
|
+
payload.ai_model_id = `policy-downgrade`;
|
|
2782
|
+
payload.ai_context = {
|
|
2783
|
+
provider: "policy-monitor",
|
|
2784
|
+
expected_version: this._lastKnownGoodVersion,
|
|
2785
|
+
loaded_version: options.policyVersion,
|
|
2786
|
+
content_hash: options.policyContentHash,
|
|
2787
|
+
downgrade: true,
|
|
2788
|
+
};
|
|
2789
|
+
}
|
|
2790
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2791
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2792
|
+
this.buffer.enqueueMany([payload]);
|
|
2793
|
+
if (options.strict) {
|
|
2794
|
+
throw new Error(`SWT3: Policy downgrade detected: version ${options.policyVersion} < ${this._lastKnownGoodVersion}`);
|
|
2795
|
+
}
|
|
2796
|
+
return payload;
|
|
2797
|
+
}
|
|
2798
|
+
// ── Governance Layer Registration (AI-METAGOV.2) ──────────────────────
|
|
2799
|
+
/**
|
|
2800
|
+
* Register an AI governance layer with the witness layer (AI-METAGOV.2).
|
|
2801
|
+
*
|
|
2802
|
+
* Each AI governance layer must register before its outputs are
|
|
2803
|
+
* considered authoritative. Computes governance stack fingerprint
|
|
2804
|
+
* with SWT3:GOVSTACK: domain separator.
|
|
2805
|
+
*/
|
|
2806
|
+
registerGovernanceLayer(options) {
|
|
2807
|
+
const stackInput = `${options.layerId}:${options.configHash}:${options.stackPosition}`;
|
|
2808
|
+
const regFingerprint = sha256Truncated(`SWT3:GOVSTACK:${stackInput}`, 12);
|
|
2809
|
+
const fa = 1;
|
|
2810
|
+
const fb = parseInt(regFingerprint.slice(0, 8), 16) % 1000000;
|
|
2811
|
+
const fc = options.stackPosition;
|
|
2812
|
+
const [ts, epoch] = timestampMs();
|
|
2813
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.2", fa, fb, fc, ts);
|
|
2814
|
+
const payload = {
|
|
2815
|
+
procedure_id: "AI-METAGOV.2", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2816
|
+
clearing_level: this.config.clearingLevel,
|
|
2817
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2818
|
+
};
|
|
2819
|
+
if (this.config.clearingLevel <= 1) {
|
|
2820
|
+
payload.ai_model_id = options.modelId ?? options.layerId;
|
|
2821
|
+
const ctx = {
|
|
2822
|
+
provider: "governance-layer-registration",
|
|
2823
|
+
layer_id: options.layerId,
|
|
2824
|
+
config_hash: options.configHash,
|
|
2825
|
+
stack_position: options.stackPosition,
|
|
2826
|
+
registration_fingerprint: regFingerprint,
|
|
2827
|
+
};
|
|
2828
|
+
if (options.modelId)
|
|
2829
|
+
ctx.model_id = options.modelId;
|
|
2830
|
+
payload.ai_context = ctx;
|
|
2831
|
+
}
|
|
2832
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2833
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2834
|
+
this.buffer.enqueueMany([payload]);
|
|
2835
|
+
return payload;
|
|
2836
|
+
}
|
|
2837
|
+
/**
|
|
2838
|
+
* Witness an AI governance layer's output/verdict (AI-METAGOV.2).
|
|
2839
|
+
*
|
|
2840
|
+
* Records the governance layer's compliance decision in the non-AI
|
|
2841
|
+
* witness layer, creating an independently verifiable record.
|
|
2842
|
+
*/
|
|
2843
|
+
witnessGovernanceOutput(options) {
|
|
2844
|
+
const fa = 1;
|
|
2845
|
+
const fb = safeHexInt(options.evidenceHash);
|
|
2846
|
+
const fc = options.verdict === "PASS" ? 1 : 0;
|
|
2847
|
+
const [ts, epoch] = timestampMs();
|
|
2848
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.2", fa, fb, fc, ts);
|
|
2849
|
+
const payload = {
|
|
2850
|
+
procedure_id: "AI-METAGOV.2", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2851
|
+
clearing_level: this.config.clearingLevel,
|
|
2852
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2853
|
+
};
|
|
2854
|
+
if (this.config.clearingLevel <= 1) {
|
|
2855
|
+
payload.ai_model_id = options.modelId ?? options.layerId;
|
|
2856
|
+
payload.ai_context = {
|
|
2857
|
+
provider: "governance-output",
|
|
2858
|
+
layer_id: options.layerId,
|
|
2859
|
+
verdict: options.verdict,
|
|
2860
|
+
evidence_hash: options.evidenceHash,
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2863
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2864
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2865
|
+
this.buffer.enqueueMany([payload]);
|
|
2866
|
+
return payload;
|
|
2867
|
+
}
|
|
2868
|
+
// ── Governance Authorization (AI-METAGOV.5) ──────────────────────
|
|
2869
|
+
/**
|
|
2870
|
+
* Authorize a governance configuration change (AI-METAGOV.5).
|
|
2871
|
+
*
|
|
2872
|
+
* Records the operator identity, authority scope, and signature
|
|
2873
|
+
* for governance changes. Supports separation of duties enforcement.
|
|
2874
|
+
*/
|
|
2875
|
+
authorizeGovernanceChange(options) {
|
|
2876
|
+
const fa = METAGOV_SCOPE_CODES[options.scopeDomain] ?? 0;
|
|
2877
|
+
const fb = METAGOV_PERMISSION_CODES[options.permissionLevel] ?? 0;
|
|
2878
|
+
const fc = safeHexInt(options.operatorCredentialHash);
|
|
2879
|
+
const [ts, epoch] = timestampMs();
|
|
2880
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.5", fa, fb, fc, ts);
|
|
2881
|
+
const payload = {
|
|
2882
|
+
procedure_id: "AI-METAGOV.5", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2883
|
+
clearing_level: this.config.clearingLevel,
|
|
2884
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2885
|
+
};
|
|
2886
|
+
if (this.config.clearingLevel <= 1) {
|
|
2887
|
+
payload.ai_model_id = `governance-auth-${options.scopeDomain}`;
|
|
2888
|
+
payload.ai_context = {
|
|
2889
|
+
provider: "governance-authorization",
|
|
2890
|
+
scope_domain: options.scopeDomain,
|
|
2891
|
+
permission_level: options.permissionLevel,
|
|
2892
|
+
operator_id: options.operatorId,
|
|
2893
|
+
change_description: options.changeDescription,
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2897
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2898
|
+
this.buffer.enqueueMany([payload]);
|
|
2899
|
+
return payload;
|
|
2900
|
+
}
|
|
2901
|
+
// ── Emergency Override Attestation (AI-METAGOV.6) ──────────────────
|
|
2902
|
+
/**
|
|
2903
|
+
* Witness an emergency governance override (AI-METAGOV.6).
|
|
2904
|
+
*
|
|
2905
|
+
* Records governance changes made outside normal approval workflow.
|
|
2906
|
+
* Triggers mandatory review requirement.
|
|
2907
|
+
*/
|
|
2908
|
+
witnessEmergencyOverride(options) {
|
|
2909
|
+
const fa = METAGOV_OVERRIDE_REASON_CODES[options.overrideReason] ?? 0;
|
|
2910
|
+
const fb = options.reviewWindowHours;
|
|
2911
|
+
const fc = METAGOV_REVIEW_STATUS_CODES.unreviewed; // always starts unreviewed
|
|
2912
|
+
const [ts, epoch] = timestampMs();
|
|
2913
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.6", fa, fb, fc, ts);
|
|
2914
|
+
const payload = {
|
|
2915
|
+
procedure_id: "AI-METAGOV.6", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2916
|
+
clearing_level: this.config.clearingLevel,
|
|
2917
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2918
|
+
};
|
|
2919
|
+
if (this.config.clearingLevel <= 1) {
|
|
2920
|
+
payload.ai_model_id = `emergency-override`;
|
|
2921
|
+
payload.ai_context = {
|
|
2922
|
+
provider: "governance-emergency",
|
|
2923
|
+
override_reason: options.overrideReason,
|
|
2924
|
+
review_window_hours: options.reviewWindowHours,
|
|
2925
|
+
operator_id: options.operatorId,
|
|
2926
|
+
change_description: options.changeDescription,
|
|
2927
|
+
review_status: "unreviewed",
|
|
2928
|
+
};
|
|
2929
|
+
}
|
|
2930
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2931
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2932
|
+
this.buffer.enqueueMany([payload]);
|
|
2933
|
+
return payload;
|
|
2934
|
+
}
|
|
2935
|
+
// ── Governance Sync Verification (AI-METAGOV.7) ──────────────────────
|
|
2936
|
+
/**
|
|
2937
|
+
* Witness governance policy divergence between organizations (AI-METAGOV.7).
|
|
2938
|
+
*
|
|
2939
|
+
* Records whether federated organizations have equivalent, compatible,
|
|
2940
|
+
* or divergent governance policies during trust credential exchange.
|
|
2941
|
+
*/
|
|
2942
|
+
witnessGovernanceSync(options) {
|
|
2943
|
+
const fa = METAGOV_DIVERGENCE_CODES[options.divergenceType] ?? 0;
|
|
2944
|
+
const fb = safeHexInt(options.localPolicyHash);
|
|
2945
|
+
const fc = safeHexInt(options.remotePolicyHash);
|
|
2946
|
+
const [ts, epoch] = timestampMs();
|
|
2947
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.7", fa, fb, fc, ts);
|
|
2948
|
+
const payload = {
|
|
2949
|
+
procedure_id: "AI-METAGOV.7", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2950
|
+
clearing_level: this.config.clearingLevel,
|
|
2951
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2952
|
+
};
|
|
2953
|
+
if (this.config.clearingLevel <= 1) {
|
|
2954
|
+
payload.ai_model_id = `governance-sync`;
|
|
2955
|
+
const ctx = {
|
|
2956
|
+
provider: "governance-sync",
|
|
2957
|
+
divergence_type: options.divergenceType,
|
|
2958
|
+
local_policy_hash: options.localPolicyHash,
|
|
2959
|
+
remote_policy_hash: options.remotePolicyHash,
|
|
2960
|
+
};
|
|
2961
|
+
if (options.remoteTenantId)
|
|
2962
|
+
ctx.remote_tenant_id = options.remoteTenantId;
|
|
2963
|
+
payload.ai_context = ctx;
|
|
2964
|
+
}
|
|
2965
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
2966
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
2967
|
+
this.buffer.enqueueMany([payload]);
|
|
2968
|
+
return payload;
|
|
2969
|
+
}
|
|
2970
|
+
// ── Attestation Purity Verification (AI-METAGOV.8) ──────────────────
|
|
2971
|
+
/**
|
|
2972
|
+
* Verify and attest that the attestation engine is free of AI (AI-METAGOV.8).
|
|
2973
|
+
*
|
|
2974
|
+
* Computes a combined hash of attestation path source files to prove
|
|
2975
|
+
* the witness layer contains no machine learning components.
|
|
2976
|
+
*/
|
|
2977
|
+
verifyAttestationPurity(options) {
|
|
2978
|
+
const combinedInput = options.sourceFiles
|
|
2979
|
+
.sort((a, b) => a.path.localeCompare(b.path))
|
|
2980
|
+
.map(f => `${f.path}:${f.hash}`)
|
|
2981
|
+
.join("|");
|
|
2982
|
+
const combinedHash = sha256Truncated(combinedInput, 12);
|
|
2983
|
+
const fa = options.sourceFiles.length;
|
|
2984
|
+
const fb = parseInt(combinedHash.slice(0, 8), 16) % 1000000;
|
|
2985
|
+
const fc = 1; // pure by definition (this SDK has no AI in attestation path)
|
|
2986
|
+
const [ts, epoch] = timestampMs();
|
|
2987
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-METAGOV.8", fa, fb, fc, ts);
|
|
2988
|
+
const payload = {
|
|
2989
|
+
procedure_id: "AI-METAGOV.8", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
2990
|
+
clearing_level: this.config.clearingLevel,
|
|
2991
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
2992
|
+
};
|
|
2993
|
+
if (this.config.clearingLevel <= 1) {
|
|
2994
|
+
payload.ai_model_id = `attestation-purity`;
|
|
2995
|
+
const ctx = {
|
|
2996
|
+
provider: "purity-verification",
|
|
2997
|
+
source_file_count: options.sourceFiles.length,
|
|
2998
|
+
combined_source_hash: combinedHash,
|
|
2999
|
+
purity_tier: "verified_pure",
|
|
3000
|
+
};
|
|
3001
|
+
if (options.buildHash)
|
|
3002
|
+
ctx.build_hash = options.buildHash;
|
|
3003
|
+
payload.ai_context = ctx;
|
|
3004
|
+
}
|
|
3005
|
+
const policyHash = this.config.policyVersion ? sha256Truncated(this.config.policyVersion, 12) : undefined;
|
|
3006
|
+
this._applyOperationalMetadata(payload, policyHash);
|
|
3007
|
+
this.buffer.enqueueMany([payload]);
|
|
3008
|
+
return payload;
|
|
3009
|
+
}
|
|
3010
|
+
// ── Physical AI / Large Engineering Model (AI-ENG.1-5) ──────────
|
|
3011
|
+
/**
|
|
3012
|
+
* Witness AI-generated design provenance (AI-ENG.1).
|
|
3013
|
+
* DO-178C 5.1, ASME V&V 10 3.1, FDA 21 CFR 11.10(a).
|
|
3014
|
+
*/
|
|
3015
|
+
witnessDesignProvenance(options) {
|
|
3016
|
+
const fa = options.constraintsApplied;
|
|
3017
|
+
const fb = options.parametersGenerated;
|
|
3018
|
+
const fc = DESIGN_DOMAIN_CODES[options.designDomain] ?? 7;
|
|
3019
|
+
const [ts, epoch] = timestampMs();
|
|
3020
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-ENG.1", fa, fb, fc, ts);
|
|
3021
|
+
const payload = {
|
|
3022
|
+
procedure_id: "AI-ENG.1", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
3023
|
+
clearing_level: this.config.clearingLevel,
|
|
3024
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
3025
|
+
};
|
|
3026
|
+
if (this.config.clearingLevel <= 1) {
|
|
3027
|
+
payload.ai_model_id = `design-${options.designDomain}`;
|
|
3028
|
+
const ctx = { provider: "design-generation", design_domain: options.designDomain };
|
|
3029
|
+
if (options.designHash)
|
|
3030
|
+
ctx.design_hash = options.designHash;
|
|
3031
|
+
if (options.modelVersion)
|
|
3032
|
+
ctx.model_version = options.modelVersion;
|
|
3033
|
+
payload.ai_context = ctx;
|
|
3034
|
+
}
|
|
3035
|
+
this.buffer.enqueueMany([payload]);
|
|
3036
|
+
return payload;
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* Witness simulation validation of AI-generated design (AI-ENG.2).
|
|
3040
|
+
* DO-178C 6.3, ASME V&V 10 4.1, ISO 26262-4.
|
|
3041
|
+
*/
|
|
3042
|
+
witnessSimulationValidation(options) {
|
|
3043
|
+
const fa = options.simulationsRun;
|
|
3044
|
+
const fb = options.simulationsPassed;
|
|
3045
|
+
const fc = SIMULATION_TYPE_CODES[options.simulationType] ?? 6;
|
|
3046
|
+
const [ts, epoch] = timestampMs();
|
|
3047
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-ENG.2", fa, fb, fc, ts);
|
|
3048
|
+
const payload = {
|
|
3049
|
+
procedure_id: "AI-ENG.2", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
3050
|
+
clearing_level: this.config.clearingLevel,
|
|
3051
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
3052
|
+
};
|
|
3053
|
+
if (this.config.clearingLevel <= 1) {
|
|
3054
|
+
payload.ai_model_id = `sim-${options.simulationType}`;
|
|
3055
|
+
const ctx = { provider: "simulation-validation", simulation_type: options.simulationType };
|
|
3056
|
+
if (options.simulationHash)
|
|
3057
|
+
ctx.simulation_hash = options.simulationHash;
|
|
3058
|
+
if (options.acceptanceCriteria)
|
|
3059
|
+
ctx.acceptance_criteria = options.acceptanceCriteria;
|
|
3060
|
+
payload.ai_context = ctx;
|
|
3061
|
+
}
|
|
3062
|
+
this.buffer.enqueueMany([payload]);
|
|
3063
|
+
return payload;
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Witness safety-critical review gate (AI-ENG.3).
|
|
3067
|
+
* DO-178C 7.2, FDA 21 CFR 11.10(g), ISO 26262-2.
|
|
3068
|
+
*/
|
|
3069
|
+
witnessSafetyReview(options) {
|
|
3070
|
+
const fa = options.reviewersRequired;
|
|
3071
|
+
const fb = options.reviewersApproved;
|
|
3072
|
+
const fc = APPROVAL_TYPE_CODES[options.approvalType] ?? 0;
|
|
3073
|
+
const [ts, epoch] = timestampMs();
|
|
3074
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-ENG.3", fa, fb, fc, ts);
|
|
3075
|
+
const payload = {
|
|
3076
|
+
procedure_id: "AI-ENG.3", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
3077
|
+
clearing_level: this.config.clearingLevel,
|
|
3078
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
3079
|
+
};
|
|
3080
|
+
if (this.config.clearingLevel <= 1) {
|
|
3081
|
+
payload.ai_model_id = `review-${options.approvalType}`;
|
|
3082
|
+
const ctx = { provider: "safety-review", approval_type: options.approvalType };
|
|
3083
|
+
if (options.reviewId)
|
|
3084
|
+
ctx.review_id = options.reviewId;
|
|
3085
|
+
if (options.peLicense)
|
|
3086
|
+
ctx.pe_license = options.peLicense;
|
|
3087
|
+
payload.ai_context = ctx;
|
|
3088
|
+
}
|
|
3089
|
+
this.buffer.enqueueMany([payload]);
|
|
3090
|
+
return payload;
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Witness material specification compliance (AI-ENG.4).
|
|
3094
|
+
* ASME V&V 10 3.3, DO-254 5.3, ISO 26262-8.
|
|
3095
|
+
*/
|
|
3096
|
+
witnessMaterialCompliance(options) {
|
|
3097
|
+
const fa = options.specificationsChecked;
|
|
3098
|
+
const fb = options.specificationsMet;
|
|
3099
|
+
const fc = MATERIAL_STANDARD_CODES[options.standard] ?? 6;
|
|
3100
|
+
const [ts, epoch] = timestampMs();
|
|
3101
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-ENG.4", fa, fb, fc, ts);
|
|
3102
|
+
const payload = {
|
|
3103
|
+
procedure_id: "AI-ENG.4", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
3104
|
+
clearing_level: this.config.clearingLevel,
|
|
3105
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
3106
|
+
};
|
|
3107
|
+
if (this.config.clearingLevel <= 1) {
|
|
3108
|
+
payload.ai_model_id = `material-${options.standard}`;
|
|
3109
|
+
const ctx = { provider: "material-compliance", standard: options.standard };
|
|
3110
|
+
if (options.materialId)
|
|
3111
|
+
ctx.material_id = options.materialId;
|
|
3112
|
+
if (options.specificationRef)
|
|
3113
|
+
ctx.specification_ref = options.specificationRef;
|
|
3114
|
+
payload.ai_context = ctx;
|
|
3115
|
+
}
|
|
3116
|
+
this.buffer.enqueueMany([payload]);
|
|
3117
|
+
return payload;
|
|
3118
|
+
}
|
|
3119
|
+
/**
|
|
3120
|
+
* Witness design revision chain (AI-ENG.5).
|
|
3121
|
+
* DO-178C 7.3, FDA 21 CFR 11.10(e), ASME V&V 10 2.4.
|
|
3122
|
+
*/
|
|
3123
|
+
witnessDesignChain(options) {
|
|
3124
|
+
const fa = options.totalRevisions;
|
|
3125
|
+
const fb = options.aiGeneratedRevisions;
|
|
3126
|
+
const fc = CHAIN_STATUS_CODES[options.chainStatus] ?? 0;
|
|
3127
|
+
const [ts, epoch] = timestampMs();
|
|
3128
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-ENG.5", fa, fb, fc, ts);
|
|
3129
|
+
const payload = {
|
|
3130
|
+
procedure_id: "AI-ENG.5", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
3131
|
+
clearing_level: this.config.clearingLevel,
|
|
3132
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
3133
|
+
};
|
|
3134
|
+
if (this.config.clearingLevel <= 1) {
|
|
3135
|
+
payload.ai_model_id = `design-chain-${options.chainStatus}`;
|
|
3136
|
+
const ctx = { provider: "design-revision-chain", chain_status: options.chainStatus, ai_revision_ratio: options.aiGeneratedRevisions / Math.max(options.totalRevisions, 1) };
|
|
3137
|
+
if (options.designId)
|
|
3138
|
+
ctx.design_id = options.designId;
|
|
3139
|
+
if (options.finalHash)
|
|
3140
|
+
ctx.final_hash = options.finalHash;
|
|
3141
|
+
payload.ai_context = ctx;
|
|
3142
|
+
}
|
|
3143
|
+
this.buffer.enqueueMany([payload]);
|
|
3144
|
+
return payload;
|
|
3145
|
+
}
|
|
3146
|
+
/**
|
|
3147
|
+
* Witness fabrication release attestation (AI-ENG.6).
|
|
3148
|
+
* DO-178C 5.5, FDA 21 CFR 11.10(f), ISO 26262-4 7.4.4.
|
|
3149
|
+
*/
|
|
3150
|
+
witnessFabricationRelease(options) {
|
|
3151
|
+
const fa = options.designHashVerified ? 1 : 0;
|
|
3152
|
+
const fb = options.authorizationCount;
|
|
3153
|
+
const fc = RELEASE_TYPE_CODES[options.releaseType] ?? 0;
|
|
3154
|
+
const [ts, epoch] = timestampMs();
|
|
3155
|
+
const fp = mintFingerprint(this.config.tenantId, "AI-ENG.6", fa, fb, fc, ts);
|
|
3156
|
+
const payload = {
|
|
3157
|
+
procedure_id: "AI-ENG.6", factor_a: fa, factor_b: fb, factor_c: fc,
|
|
3158
|
+
clearing_level: this.config.clearingLevel,
|
|
3159
|
+
anchor_fingerprint: fp, anchor_epoch: epoch, fingerprint_timestamp_ms: ts,
|
|
3160
|
+
};
|
|
3161
|
+
if (this.config.clearingLevel <= 1) {
|
|
3162
|
+
payload.ai_model_id = `fabrication-${options.releaseType}`;
|
|
3163
|
+
const ctx = { provider: "fabrication-release", release_type: options.releaseType, design_hash_verified: options.designHashVerified };
|
|
3164
|
+
if (options.productionSystemId)
|
|
3165
|
+
ctx.production_system_id = options.productionSystemId;
|
|
3166
|
+
if (options.approvedDesignHash)
|
|
3167
|
+
ctx.approved_design_hash = options.approvedDesignHash;
|
|
3168
|
+
if (options.finalDesignHash)
|
|
3169
|
+
ctx.final_design_hash = options.finalDesignHash;
|
|
3170
|
+
payload.ai_context = ctx;
|
|
3171
|
+
}
|
|
3172
|
+
this.buffer.enqueueMany([payload]);
|
|
3173
|
+
return payload;
|
|
3174
|
+
}
|
|
3175
|
+
// ── Trust Mesh (AI-TRUST.1 / AI-TRUST.2) ────────────────────────
|
|
3176
|
+
_trustRegistry;
|
|
1899
3177
|
_witnessedProcedures = new Set();
|
|
1900
3178
|
get trustRegistry() {
|
|
1901
3179
|
if (!this._trustRegistry)
|
|
@@ -2115,4 +3393,81 @@ export class Witness {
|
|
|
2115
3393
|
return this.buffer.receipts;
|
|
2116
3394
|
}
|
|
2117
3395
|
}
|
|
3396
|
+
/**
|
|
3397
|
+
* Validate a governance rule graph for circular dependencies (AI-METAGOV.4).
|
|
3398
|
+
*
|
|
3399
|
+
* Uses Kahn's algorithm (BFS topological sort) to detect cycles in governance
|
|
3400
|
+
* rule dependency graphs. Returns validation results including any detected cycles.
|
|
3401
|
+
*/
|
|
3402
|
+
export function validateGovernanceGraph(rules) {
|
|
3403
|
+
// Build adjacency list and in-degree map
|
|
3404
|
+
const adj = new Map();
|
|
3405
|
+
const inDegree = new Map();
|
|
3406
|
+
for (const rule of rules) {
|
|
3407
|
+
if (!adj.has(rule.id))
|
|
3408
|
+
adj.set(rule.id, []);
|
|
3409
|
+
if (!inDegree.has(rule.id))
|
|
3410
|
+
inDegree.set(rule.id, 0);
|
|
3411
|
+
for (const dep of rule.dependencies ?? []) {
|
|
3412
|
+
if (!adj.has(dep))
|
|
3413
|
+
adj.set(dep, []);
|
|
3414
|
+
if (!inDegree.has(dep))
|
|
3415
|
+
inDegree.set(dep, 0);
|
|
3416
|
+
adj.get(dep).push(rule.id);
|
|
3417
|
+
inDegree.set(rule.id, (inDegree.get(rule.id) ?? 0) + 1);
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
// Kahn's algorithm
|
|
3421
|
+
const queue = [];
|
|
3422
|
+
for (const [node, deg] of inDegree) {
|
|
3423
|
+
if (deg === 0)
|
|
3424
|
+
queue.push(node);
|
|
3425
|
+
}
|
|
3426
|
+
const sorted = [];
|
|
3427
|
+
const depths = new Map();
|
|
3428
|
+
for (const node of queue)
|
|
3429
|
+
depths.set(node, 0);
|
|
3430
|
+
while (queue.length > 0) {
|
|
3431
|
+
const node = queue.shift();
|
|
3432
|
+
sorted.push(node);
|
|
3433
|
+
for (const neighbor of adj.get(node) ?? []) {
|
|
3434
|
+
const newDeg = (inDegree.get(neighbor) ?? 1) - 1;
|
|
3435
|
+
inDegree.set(neighbor, newDeg);
|
|
3436
|
+
depths.set(neighbor, Math.max(depths.get(neighbor) ?? 0, (depths.get(node) ?? 0) + 1));
|
|
3437
|
+
if (newDeg === 0)
|
|
3438
|
+
queue.push(neighbor);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
// Nodes not in sorted = part of cycles
|
|
3442
|
+
const cycleNodes = new Set();
|
|
3443
|
+
for (const [node, deg] of inDegree) {
|
|
3444
|
+
if (deg > 0)
|
|
3445
|
+
cycleNodes.add(node);
|
|
3446
|
+
}
|
|
3447
|
+
// Extract cycle paths from remaining nodes
|
|
3448
|
+
const cycles = [];
|
|
3449
|
+
if (cycleNodes.size > 0) {
|
|
3450
|
+
const visited = new Set();
|
|
3451
|
+
for (const start of cycleNodes) {
|
|
3452
|
+
if (visited.has(start))
|
|
3453
|
+
continue;
|
|
3454
|
+
const cycle = [];
|
|
3455
|
+
let current = start;
|
|
3456
|
+
while (current && !visited.has(current) && cycleNodes.has(current)) {
|
|
3457
|
+
visited.add(current);
|
|
3458
|
+
cycle.push(current);
|
|
3459
|
+
current = (adj.get(current) ?? []).find(n => cycleNodes.has(n) && !visited.has(n));
|
|
3460
|
+
}
|
|
3461
|
+
if (cycle.length > 0)
|
|
3462
|
+
cycles.push(cycle);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
const maxDepth = Math.max(0, ...Array.from(depths.values()));
|
|
3466
|
+
return {
|
|
3467
|
+
valid: cycles.length === 0,
|
|
3468
|
+
cycles,
|
|
3469
|
+
maxDepth,
|
|
3470
|
+
ruleCount: rules.length,
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
2118
3473
|
//# sourceMappingURL=witness.js.map
|