// reguli.mjs: which verifiers the conformance suite enrols, the rules each of them NAMES (derived from its code, never typed here as // a list of its own), the public id and the level of each rule. // // A verifier that lives in this repository as a module exports `REGULI` (the rules it can name; its helper throws on any other) and // carries a /*PAZA:*/ marker on every check of a rule. The derivation imports the module, reads its source, and requires: // declared == marked == mapped below. A rule added to a verifier and not mapped here, or mapped here and gone from the verifier, is a // refusal of the derivation, not a silent drift. The AIP-23 reference verifier (verify-proof.mjs) is a published program, not edited // for this suite: its rules are its level names, read from its `level('', ... 'FAILED'` sites, and every FAILED site of its // `finality` level must be classified below (anchoring or finality) or the derivation refuses. import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; const AICI = path.dirname(fileURLToPath(import.meta.url)); export const LEVELS = ['integrity', 'authenticity', 'anchoring', 'finality']; /** The AIP-23 reference verifier: AERE_VERIFY_PROOF= (published with aere-node), or the development repository. */ export const VERIFY_PROOF = process.env.AERE_VERIFY_PROOF || path.resolve(AICI, '..', '..', 'verificator-falcon', 'verify-proof.mjs'); /** The modules judged in-process. `env` names the variable through which the planting control substitutes a planted copy. */ export const MODULE = { risc: { url: new URL('../payments/risc.mjs', import.meta.url), env: 'AERE_CONF_MODUL_RISC' }, chitanta: { url: new URL('../payments/chitanta.mjs', import.meta.url), env: 'AERE_CONF_MODUL_CHITANTA' }, piata: { url: new URL('../market-settlement/piata.mjs', import.meta.url), env: 'AERE_CONF_MODUL_PIATA' }, baza: { url: new URL('../verifiable-data/baza.mjs', import.meta.url), env: 'AERE_CONF_MODUL_BAZA' }, aitrust: { url: new URL('../ai-trust/lant-de-incredere.mjs', import.meta.url), env: 'AERE_CONF_MODUL_AITRUST' }, sidecar: { url: new URL('../verify-layer/sidecar.mjs', import.meta.url), env: 'AERE_CONF_MODUL_SIDECAR' }, arbore: { url: new URL('../verify-layer/jurnal-arbore.mjs', import.meta.url), env: 'AERE_CONF_MODUL_ARBORE' }, ledger: { url: new URL('../agents/agent-ledger.mjs', import.meta.url), env: 'AERE_CONF_MODUL_LEDGER' }, }; export const caleModul = (k) => process.env[MODULE[k].env] || fileURLToPath(MODULE[k].url); export const incarca = (k) => import(pathToFileURL(caleModul(k)).href); const I = 'integrity', AU = 'authenticity', AN = 'anchoring'; /** * How a verifier MARKS its rules in code. `paza`: a comment marker on every check of a rule (the planting makes the marked check true). * `apel`: every refusal names its rule where it calls the verifier's one refusal helper (`apel.re` finds those calls; `apel.planteaza` * rewrites that helper, in a copy, so that it drops the refusals of one rule). Either way a rule is planted out WHOLE, in a copy. */ const APEL = { aitrust: { re: /(?:\bfail|fail : unm\))\('[a-z]+', '([a-z-]+)'/g, planteaza: (src, r) => { const a = "const fail = (l, reason, detail) => { if (!REGULI.includes(reason)) throw new Error('ai-trust: undeclared rule ' + reason); "; return src.split(a).length === 2 ? src.replace(a, () => a + `if (reason === '${r}') return; `) : null; } }, }; export const marcajul = (sursa) => APEL[sursa] || null; /** verifier -> { artifacts it judges, where its rules come from, source rule -> [public id, level, what the rule says] } */ export const VERIFICATOARE = { 'aip23-envelope': { artifacts: ['envelope'], sursa: 'verify-proof', reguli: { form: ['envelope.form', I, 'the envelope has a statement (object) and a statementHash (0x + 64 hex)'], integrity: ['envelope.integrity', I, 'sha256 of the canonical statement text is the statementHash'], signature: ['envelope.signature', 'authenticity', 'the ML-DSA signature the envelope carries verifies over the canonical statement text'], finality: ['envelope.finality', 'anchoring', 'the statementHash is recorded in the notary under a state root that a verified post-quantum anchor certificate covers (level by site: see finalitySites)'] } }, 'payment-risk': { artifacts: ['payment-risk'], sursa: 'risc', reguli: { forma: ['risk.form', I, 'payer and payee are addresses; chain, asset, a positive integer amount and the time are given'], activ: ['risk.asset', I, 'the asset on that chain is allowed by the declared policy'], contrapartida: ['risk.counterparty', I, 'the payee is not the payer and is not a refused counterparty of the policy'], curs: ['risk.rate', I, 'the USD rate is declared (value, source, time) when the policy requires it'], limita: ['risk.limit', I, 'the amount in USD is within the per-payment limit of the policy'], fereastra: ['risk.window', I, 'the amount and the count stay within the sliding window of the policy'] } }, 'payment-receipt': { artifacts: ['payment-receipt'], sursa: 'chitanta', reguli: { legatura: ['receipt.binding', I, 'the settlement envelope commits to this instruction and says the same thing'], suma: ['receipt.amount', I, 'the USD amount is the amount at the declared rate, exactly'], politica: ['receipt.policy', I, 'the policy shown hashes to the policy the risk verdict names'], risc: ['receipt.risk', I, 'the risk verdict reproduces from the inputs and the policy, and it allowed this payment'], lant: ['receipt.chain', I, 'the transaction supplied is this payment: payer, payee, value, block, mined'] } }, 'market-timeline': { artifacts: ['market-timeline'], sursa: 'piata', reguli: { forma: ['market.form', I, 'the timeline is well formed: events with name, arguments, sender, integer time and block; the job and the constants with their integer fields'], intreg: ['market.complete', I, 'the timeline carries every event the job state implies: posted; claimed when a provider is set; submitted and disputed when their times are set'], postare: ['market.posting', I, 'the JobPosted event matches the job (requester, spec, deadline, reward)'], termen: ['market.deadline', I, 'claim and submission are within the deadline; an expiry refund only after it'], fereastra: ['market.window', I, 'a dispute is raised inside the challenge window; a third party finalizes only after it'], arbitru: ['market.arbiter', I, 'a dispute is resolved by the arbiter, or timed out only after the arbitration timeout'], plata: ['market.payout', I, 'the terminal payout matches the final state (who, how much, which bond)'], rezultat: ['market.result', I, 'the result bytes hash to the commitment on chain'], replay: ['market.replay', I, 'the public task recomputed agrees with who was paid or slashed'], dovada: ['market.proof', I, 'the execution envelope names this job and existed before the window closed'] } }, dataset: { artifacts: ['dataset-record', 'dataset-answer', 'dataset-consistency', 'dataset-sample'], sursa: 'baza', reguli: { forma: ['dataset.form', I, 'a row carries its field commitments and its disclosed fields'], angajament: ['dataset.commitment', I, 'the record, answer or proof names the commitment it is checked against'], camp: ['dataset.field', I, 'a disclosed value opens its salted field commitment'], includere: ['dataset.inclusion', I, 'the record is in the log of that version (RFC 9162 inclusion)'], index: ['dataset.index', I, 'the row is at its stated position of the sorted index'], interval: ['dataset.range', I, 'every row returned is inside the queried range'], ordine: ['dataset.order', I, 'consecutive rows are in index order'], completitudine: ['dataset.completeness', I, 'the rows are the whole contiguous run; the neighbours on both sides are outside the range'], consistenta: ['dataset.consistency', I, 'the older log is a prefix of the newer (RFC 9162 consistency) and the versions are linked'], esantion: ['dataset.sample', I, 'every sampled position is served and verifies'] } }, 'ai-trust': { artifacts: ['ai-trust-bundle'], sursa: 'aitrust', reguli: { form: ['aitrust.form', I, 'the bundle has the format, an identity, a ledger with its policy, and a Proof of AI envelope'], 'identity-mismatch': ['aitrust.identity-mismatch', AU, 'the id derived from the key is the one the bundle, the ledger and the output name'], 'identity-key': ['aitrust.identity-key', AU, 'the public key of the identity can be read'], 'version-attestation': ['aitrust.version-attestation', I, 'the Proof of Software attestation of the model artifact verifies as an envelope'], 'version-artifact-mismatch': ['aitrust.version-artifact-mismatch', I, 'the output names model weights that the attestation covers'], 'version-label-mismatch': ['aitrust.version-label-mismatch', I, 'the output and the attestation name the same model version'], 'runtime-record': ['aitrust.runtime-record', I, 'the runtime record is a verified runtime attestation'], 'runtime-artifact-mismatch': ['aitrust.runtime-artifact-mismatch', I, 'the host ran the artifact the output names'], 'runtime-after-output': ['aitrust.runtime-after-output', I, 'the runtime record is not dated after the output'], 'audit-log-broken': ['aitrust.audit-log-broken', I, 'the hash chain of the audit log given is intact'], 'runtime-not-in-audit-log': ['aitrust.runtime-not-in-audit-log', I, 'the runtime record is an entry of the audit log given'], 'audit-head': ['aitrust.audit-head', I, 'the audit log continues the attested head given'], 'witness-head': ['aitrust.witness-head', AN, 'every notarized head given is a head of this ledger, recorded on the chain'], 'policy-hash': ['aitrust.policy-hash', I, 'the policy given hashes to the policy hash the bundle pins'], 'ledger-invalid': ['aitrust.ledger-invalid', I, 'the ledger verifies under its policy (signatures, chain, every decision re-derived)'], 'output-not-in-ledger': ['aitrust.output-not-in-ledger', I, 'a ledger entry links this Proof of AI envelope'], 'action-refused': ['aitrust.action-refused', I, 'the ledger entry of the output was allowed by the policy'], 'ledger-time-mismatch': ['aitrust.ledger-time-mismatch', I, 'the ledger entry and the output are dated within the tolerance'], policy: ['aitrust.policy', I, 'the policy given is a policy'], 'provenance-envelope': ['aitrust.provenance-envelope', I, 'the Proof of AI envelope verifies as an envelope of its kind'], 'provenance-finality': ['aitrust.provenance-finality', AN, 'a notarization the Proof of AI envelope claims is on the chain'] } }, 'audit-log': { artifacts: ['audit-log'], sursa: 'sidecar', reguli: { intrare: ['auditlog.entry', I, 'the log is a list, and each entry is a log entry'], seq: ['auditlog.seq', I, 'the entries are numbered from zero, without a gap'], lant: ['auditlog.chain', I, 'each entry links the hash of the one before it'], hash: ['auditlog.hash', I, 'the hash of each entry is the hash of its content'], 'cap-forma': ['auditlog.head-form', I, 'the attested head is an audit head attestation with a count and a head hash'], 'cap-hash': ['auditlog.head-hash', I, 'the statementHash of the attested head is the hash of its statement'], 'cap-nesemnat': ['auditlog.head-unsigned', AU, 'the head is signed when a signer is required'], 'cap-schema': ['auditlog.head-scheme', AU, 'the head signature is an ml-dsa-65 signature, made with an ML-DSA-65 key'], 'cap-cheie': ['auditlog.head-key', AU, 'the public key of the head signature can be read'], 'cap-semnatura': ['auditlog.head-signature', AU, 'the head signature verifies over the statement'], 'cap-semnatar': ['auditlog.head-signer', AU, 'the head is signed by the expected signer'], 'cap-scurt': ['auditlog.head-short', I, 'the log has at least as many entries as the attested head covers'], 'cap-rescris': ['auditlog.head-rewritten', I, 'the entry at the attested point is the attested head (the history before it was not rewritten)'] } }, 'audit-tree': { artifacts: ['audit-inclusion', 'audit-consistency'], sursa: 'arbore', reguli: { 'dovada-forma': ['audittree.proof-form', I, 'the proof is an inclusion or a consistency proof that can be read'], 'arbore-forma': ['audittree.head-form', I, 'the tree head is a tree head attestation with a size and a root'], 'arbore-hash': ['audittree.head-hash', I, 'the statementHash of the tree head is the hash of its statement'], 'includere-index': ['audittree.inclusion-index', I, 'the entry shown is the entry of the index proven'], 'includere-intrare': ['audittree.inclusion-entry', I, 'the hash of the entry shown is the hash of its content'], 'includere-plic': ['audittree.inclusion-envelope', I, 'the envelope inside the entry hashes to its statementHash'], 'includere-frunza': ['audittree.inclusion-leaf', I, 'the leaf is the leaf of this entry'], 'includere-drum': ['audittree.inclusion-path', I, 'the path leads from the leaf to the root (RFC 9162 inclusion)'], 'includere-cap': ['audittree.inclusion-head', I, 'the proof is for the tree of the head given (size and root)'], 'consistenta-inceput': ['audittree.consistency-start', I, 'the consistency proof starts at the old head'], 'consistenta-sfarsit': ['audittree.consistency-end', I, 'the consistency proof ends at the new head'], consistenta: ['audittree.consistency', I, 'the new tree extends the old one (RFC 9162 consistency)'] } }, 'agent-ledger': { artifacts: ['agent-ledger'], sursa: 'ledger', reguli: { registru: ['agents.ledger-form', I, 'a ledger with a list of entries is given'], politica: ['agents.policy', I, 'the policy given is a policy, and hashes to the hash the verifier pins'], 'alta-politica': ['agents.other-policy', I, 'the ledger and every entry name the policy given, and the policy is this agent\'s'], sesiune: ['agents.session', I, 'the session is the one derived from the agent and the policy'], cheie: ['agents.key', AU, 'the agent key can be read and is an ML-DSA-65 key'], identitate: ['agents.identity', AU, 'the agent id is derived from the key'], ancora: ['agents.anchor', I, 'every anchor given names an entry of this ledger, with a time'], seq: ['agents.seq', I, 'the entries are numbered from zero, without a gap'], lant: ['agents.chain', I, 'each entry links the hash of the one before it'], corp: ['agents.body', I, 'the body of an entry names the agent, the session and the sequence number of its header'], semnatura: ['agents.signature', AU, 'every entry is signed by the agent key'], hash: ['agents.hash', I, 'the hash of each entry reproduces'], timp: ['agents.time', I, 'entry times do not go backwards and stay within the bounds the verifier has (its clock, a witnessed start, the anchors)'], revocare: ['agents.revocation', I, 'a revocation entry carries a revocation that verifies, and says what it carries'], aprobari: ['agents.approvals', I, 'an approvals field, when present, is a list that is not empty'], decizie: ['agents.decision', I, 'every decision written is the decision the policy gives when replayed'] } }, }; /** * The FAILED sites of verify-proof's `finality` level, classified. `fragment` must appear on exactly one such source line; `re` * matches the detail the verifier prints there; `level` is where the suite reports it. Only the last one is reachable by an honest * endpoint (a claim that is not on chain); the others need an endpoint that lies or a chain whose anchor does not verify. */ export const FINALITY_SITES = [ { fragment: 'most recent anchor did not verify', re: /^the chain's most recent anchor did not verify/, level: 'finality' }, { fragment: 'the header the endpoint served for block', re: /^the header the endpoint served for block/, level: 'anchoring' }, { fragment: 'has no account at', re: /has no account at/, level: 'anchoring' }, { fragment: 'is not the AereNotary contract', re: /is not the AereNotary contract/, level: 'anchoring' }, { fragment: 'the Merkle proof for block', re: /^the Merkle proof for block/, level: 'anchoring' }, { fragment: 'not notarized:', re: /^not notarized:/, level: 'anchoring', reachable: true }, ]; /** Verifiers published beside these that the suite does NOT enrol yet, with the pattern that counts their refusal sites. */ export const NEINROLATE = [ { name: 'identity', files: [new URL('../identity/identity.mjs', import.meta.url)], re: /\bok\(['`]/g, why: 'its checks are labelled rows, not rule ids (counted: the check rows of its presentation verifier; its SD-JWT, Travel Rule and compliance verifiers are not counted)' }, ]; /** public rule id -> { verifier, source, level, says } for every mapped rule */ export function hartaPublica() { const h = {}; for (const [v, d] of Object.entries(VERIFICATOARE)) for (const [src, [id, level, says]] of Object.entries(d.reguli)) h[id] = { verifier: v, source: src, level, says }; return h; } /** source rule ids of a verifier -> public ids */ export const public_ = (verifier, surse) => [...new Set(surse.map((s) => { const r = VERIFICATOARE[verifier].reguli[s]; if (!r) throw new Error(`${verifier}: the verifier named a rule this suite does not map: ${s}`); return r[0]; }))].sort(); /** * Derives the rule table from the code. Returns { tabel, probleme, nemasurat }: `probleme` are refusals (a rule declared and not * marked, marked and not declared, declared and not mapped, mapped and not declared, an unclassified finality site); `nemasurat` * names a verifier whose source could not be read here. `cai` substitutes the file of a module (the probe's control derives from a * planted copy to show that a rule added to a verifier and not mapped here is refused). */ export async function deriva({ cai = {} } = {}) { const probleme = [], nemasurat = []; const tabel = { v: 1, kind: 'aere-conformance-rules', levels: LEVELS, verifiers: {}, notEnrolled: [] }; for (const [v, d] of Object.entries(VERIFICATOARE)) { const rules = []; let declared = [], sites = {}, extra = {}; if (d.sursa === 'verify-proof') { if (!fs.existsSync(VERIFY_PROOF)) { nemasurat.push(`${v}: the reference verifier is not at ${VERIFY_PROOF} (AERE_VERIFY_PROOF=)`); continue; } const linii = fs.readFileSync(VERIFY_PROOF, 'utf8').split('\n').filter((l) => /level\('[a-z]+',[^;]*'FAILED'/.test(l) && !/^\s*\/\//.test(l)); for (const l of linii) for (const m of l.matchAll(/level\('([a-z]+)',[^;]*?'FAILED'/g)) sites[m[1]] = (sites[m[1]] || 0) + 1; declared = Object.keys(sites); const fin = linii.filter((l) => /level\('finality'/.test(l)); for (const s of FINALITY_SITES) { const n = fin.filter((l) => l.includes(s.fragment)).length; if (n !== 1) probleme.push(`${v}: the finality site "${s.fragment}" appears on ${n} FAILED lines of the verifier (expected 1)`); } if (fin.length !== FINALITY_SITES.length) probleme.push(`${v}: the verifier has ${fin.length} FAILED sites at its finality level, the suite classifies ${FINALITY_SITES.length}`); extra = { finalitySites: FINALITY_SITES.map((s) => ({ site: s.fragment, level: s.level, reachableWithAnHonestEndpoint: !!s.reachable })) }; } else { const p = cai[d.sursa] || fileURLToPath(MODULE[d.sursa].url); const src = fs.readFileSync(p, 'utf8'); const M = await import(pathToFileURL(p).href); if (!Array.isArray(M.REGULI)) { probleme.push(`${v}: the module does not export REGULI`); continue; } declared = [...M.REGULI]; const ap = APEL[d.sursa]; for (const m of src.matchAll(ap ? ap.re : /\/\*PAZA:([a-z-]+)\*\//g)) sites[m[1]] = (sites[m[1]] || 0) + 1; for (const r of declared) if (!sites[r]) probleme.push(`${v}: rule ${r} is declared and no ${ap ? 'refusal names it' : 'check carries its marker'}`); for (const r of Object.keys(sites)) if (!declared.includes(r)) probleme.push(`${v}: a ${ap ? 'refusal names' : 'check is marked'} ${r}, a rule the module does not declare`); if (ap) for (const r of declared) if (ap.planteaza(src, r) === null) probleme.push(`${v}: the refusal helper is not where the planting expects it`); extra = { marked: ap ? 'at the refusal helper' : 'by a marker on every check' }; } for (const r of declared) if (!d.reguli[r]) probleme.push(`${v}: the verifier names rule ${r} and the suite does not map it (a new rule needs a public id, a level and a vector)`); for (const r of Object.keys(d.reguli)) if (!declared.includes(r)) probleme.push(`${v}: the suite maps rule ${r}, which the verifier no longer names`); for (const [src, [id, level, says]] of Object.entries(d.reguli)) rules.push({ id, source: src, level, sites: sites[src] || 0, says }); tabel.verifiers[v] = { artifacts: d.artifacts, rules, ...extra }; } for (const n of NEINROLATE) { let sitesN = 0, citit = true; for (const u of n.files) { const p = fileURLToPath(u); if (!fs.existsSync(p)) { citit = false; continue; } sitesN += (fs.readFileSync(p, 'utf8').match(n.re) || []).length; } tabel.notEnrolled.push({ verifier: n.name, refusalSites: citit ? sitesN : null, why: n.why }); if (citit && sitesN === 0) probleme.push(`not enrolled ${n.name}: the pattern that counts its refusal sites finds none (the pattern is stale)`); } return { tabel, probleme, nemasurat }; }