agents: the hosted witness for agent ledgers (sandbox on testnet 28001), its probe and negative control; crypto-inventory 0.4.1 resolves a minimum TLS version given through a constant or a ternary; proof-kinds and conformance gain the agent-witness-checkpoint kind; the reproduction scripts of the Shield recipes no longer carry a workstation path

This commit is contained in:
Aere Network 2026-10-20 01:04:12 +03:00
parent 0c8f1de3fd
commit a654c3d522
27 changed files with 4737 additions and 43 deletions

View File

@ -12,13 +12,14 @@ wrote it.
Paths that DO resolve are written relative to a side-by-side checkout of the Aere repositories, so they begin with a
repository name, for example `aere-contracts/contracts/pqc/AereCryptoRegistry.sol`.
Unresolvable distinct paths in this repository: **19**.
Unresolvable distinct paths in this repository: **21**.
- `ContainerApps_ListBySubscription.json` cited in: aere-quantum/verify-layer/fixturi-cloud/README.md
- `VirtualMachine_List_MaximumSet_Gen.json` cited in: aere-quantum/verify-layer/fixturi-cloud/README.md
- `addresses.txt` cited in: aere-quantum/exposure-report/README.md
- `antete.mjs` cited in: aere-quantum/exposure-report/README.md
- `btc/antete.mjs` cited in: aere-quantum/exposure-report/README.md
- `ciob.json` cited in: aere-quantum/agents/README.md
- `data/audit.log` cited in: aere-quantum/pq-kms/README.md
- `data/root-check.json` cited in: aere-quantum/pq-kms/README.md
- `data/service.json` cited in: aere-quantum/pq-secrets/README.md
@ -27,6 +28,7 @@ Unresolvable distinct paths in this repository: **19**.
- `libs.versions.toml` cited in: aere-quantum/crypto-inventory/README.md
- `plic-aip23.json` cited in: aere-quantum/exposure-report/README.md
- `proba-client-real.mjs` cited in: aere-quantum/readiness/README.md
- `probele-b2.sh` cited in: aere-quantum/agents/README.md
- `raport.json` cited in: aere-quantum/exposure-report/README.md
- `raport.md` cited in: aere-quantum/exposure-report/README.md
- `service.json` cited in: aere-quantum/pq-secrets/README.md

View File

@ -13,10 +13,10 @@ notarization command of the verification layer and for the agents' x402 wallet (
| [`pq-pki/`](pq-pki/) | a private certificate authority for ML-DSA (X.509 v3, RFC 9881): root and issuing CAs, leaf certificates, revocation lists, and a strict chain verifier compared against OpenSSL |
| [`crypto-inventory/`](crypto-inventory/) | a cryptographic inventory of source code (JavaScript/TypeScript, Python, Java, Kotlin, Swift, Go, Rust, PEM blocks, dependency manifests including Cargo.toml, Package.swift, Podfile and Gradle): every use classified by its exposure to a quantum computer, with a migration target, written as a CycloneDX 1.6 CBOM; nothing from the scanned tree is executed, and its cost stays linear on input built to be slow |
| [`verify-layer/`](verify-layer/) | an audit-log sidecar for any deployment: entries are AIP-23 envelopes in a hash chain, the runtime adapter records every running Docker or Kubernetes container without any secret value, and from 2026-10-18 the same for what the operator's own cloud CLI exports (AWS ECS, EC2, Lambda; Azure VMs, Container Instances, Container Apps, Web Apps; GCP Compute Engine, Cloud Run), read from the export file and never from an account, and the head of the chain can be signed by the operator (ML-DSA-65) and notarized on Aere Network under a post-quantum validator certificate; it says plainly what that proves (the history before a published head) and what it does not (that the host told the truth); the same log as a Merkle tree (RFC 9162): signed tree heads, and inclusion and consistency proofs of a few hashes that anyone checks without the log |
| [`proof-kinds/`](proof-kinds/) | the AIP-23 envelope builder the verification layer uses: fifteen proof kinds (`secret-access` since 2026-10-18), one envelope format, digests instead of raw content |
| [`proof-kinds/`](proof-kinds/) | the AIP-23 envelope builder the verification layer uses: sixteen proof kinds (`secret-access` since 2026-10-18, `agent-witness-checkpoint` since 2026-10-19), one envelope format, digests instead of raw content |
| [`readiness/`](readiness/) | the post-quantum readiness scanner of a public hostname: real TLS handshakes (hybrid only, hybrid preferred, TLS 1.2), HSTS, the certificate; no connection to an address not proven public; a rate limit per client that X-Forwarded-For cannot bypass |
| [`control-plane/`](control-plane/) | from findings to a finished migration: a prioritized plan from the inventory and the scanner, its execution through the gateway, KMS and PKI with consent per action and a measured proof afterwards, recipes for the servers the products do not touch and a rescan that judges them, a compliance report against NIST IR 8547, the EU roadmap and optionally CNSA 2.0, and a console that checks it all again |
| [`agents/`](agents/) | limits an AI agent cannot break unseen: a post-quantum identity (ML-DSA-65), a policy (spending per time window, allowed tools and recipients, which actions need human approval), a signed ledger of every action judged against the policy, approvals and revocation signed by people, and a verifier that re-runs the policy over the whole ledger without trusting the agent; two branches of one ledger are a proof of equivocation anyone can check; and a wallet that pays over x402 only what the agent's ledger records and its policy allows, either holding the payment key for the owner or co-signing from a 2-of-2 contract wallet that neither the agent nor the owner can spend alone; both run on the public testnet with their evidence; and the chain as the witness of a ledger: a notarized head, read through the AIP-23 verifier under a post-quantum certified anchor, bounds entry times from below (a backdated entry is caught) |
| [`agents/`](agents/) | limits an AI agent cannot break unseen: a post-quantum identity (ML-DSA-65), a policy (spending per time window, allowed tools and recipients, which actions need human approval), a signed ledger of every action judged against the policy, approvals and revocation signed by people, and a verifier that re-runs the policy over the whole ledger without trusting the agent; two branches of one ledger are a proof of equivocation anyone can check; and a wallet that pays over x402 only what the agent's ledger records and its policy allows, either holding the payment key for the owner or co-signing from a 2-of-2 contract wallet that neither the agent nor the owner can spend alone; both run on the public testnet with their evidence; and the chain as the witness of a ledger: a notarized head, read through the AIP-23 verifier under a post-quantum certified anchor, bounds entry times from below (a backdated entry is caught); and a hosted witness (sandbox on testnet 28001; mainnet at activation): the agent sends its ledger head signed with its ML-DSA-65 key, never the ledger, the witness checks it continues the last head it holds (a conflicting head is a proof of equivocation, kept and returned), logs it in an RFC 9162 tree whose checkpoints it signs and notarizes, and returns a receipt with an inclusion proof that anyone checks without the witness and that the ledger verifier takes as an anchor |
| [`proof-of-ai/`](proof-of-ai/) | the AIP-23 envelope of an AI agent's action: model, version, weights digest, prompt, input, output, tools, agent, requester, as digests; verified by the same AIP-23 verifier as every other proof |
| [`payments/`](payments/) | the risk step and the receipt of a payment for a person or a company: a declared policy (per-payment and sliding-window limits in USD per payer, assets allowed, counterparties refused with their source) gives a verdict anyone reproduces from the same inputs, with the signals it does not have named as unmeasured (no sanctions list, no identity tier); the receipt is an instruction (payer, payee, the amount in the asset and in USD at the rate declared as an input, the transaction, the verdict with its policy under hash) committed to by an AIP-23 settlement envelope, notarized and read back under a post-quantum certified anchor; a reader checks it against the policy, the inputs and the chain with plain JSON-RPC |
| [`market-settlement/`](market-settlement/) | the dispute and settlement rail of a verifiable infrastructure market, measured end to end on testnet 28001: an order with its reward in escrow and a provider bond, the result committed on chain with its execution envelope notarized under a post-quantum certified anchor, a challenge window in which anyone disputes with a recomputed result and a bond, an arbiter that can only route the escrow (with a permissionless 14-day timeout), deadline reclaims, and an AIP-23 settlement receipt; a pure judge over the job's timeline that checks deadlines, the window, who resolved, the payout, the replay and the proof |
@ -41,13 +41,13 @@ test, counts as a failure of the control. Results measured on 2026-09-29 (Node.j
| pq-kms | 62/62 (`node test/proba.mjs`); HSM root on SoftHSM2 + OpenSC 20/20 (`pq-kms/test/proba-hsm.mjs`, Linux); sealed-file trust rules 7/7 (`pq-kms/test/proba-hsm-incredere.mjs`) | 16/16 (`node test/control-negativ.mjs`); sealed-file rules 2/2 in this repository (`pq-kms/test/control-negativ-hsm-incredere.mjs`) |
| pq-secrets | 43/43 on 2026-10-18 (`node test/proba-secrete.mjs`: the whole flow with keys born at run time, each refusal by name, a presentation signed for one request refused on any other, grant attenuation, the same refusal for every unauthorized caller, concurrent writes, rotation, revoked versions, the value searched for as a string in the log and on disk, the log intact after 100 operations, every envelope VALID for the AIP-23 reference verifier when `AERE_VERIFY_PROOF` names it, otherwise skipped by name); an adversarial review ran more than sixty attacks before publication, and the three that still succeed are limits written in the README | 43/43 on 2026-10-18 (`node test/control-negativ-secrete.mjs`: each guard planted out in a copy, the named checks must fail; `--autoproba` 5/5) |
| pq-pki | 28/28 on 2026-10-19 (`node test/proba.mjs`), each verdict compared with OpenSSL 3.5; since 2026-10-19 the signature algorithm a certificate or a revocation list declares must be its issuer key's | 26/26 (`node test/control-negativ.mjs`) |
| crypto-inventory | 68/68 (`node test/proba.mjs`, measured 2026-10-18 with the Rust, Swift and Kotlin detectors, glob imports, libcrux, BoringSSL, the libsignal Java/Node/Swift/Android APIs, CryptoKit, CommonCrypto, Security.framework, Android Keystore, Tink and OkHttp included); cost on hostile input 13/13 linear (`node test/proba-timp.mjs`) | 35/35 (`node test/control-negativ.mjs`); cost 5/5 in this repository (`node test/control-negativ-timp.mjs`; its fourth case compares with version 0.1.0 from the development history and is skipped here) |
| crypto-inventory | 68/68 (`node test/proba.mjs`, measured 2026-10-19 with the Rust, Swift and Kotlin detectors, glob imports, libcrux, BoringSSL, the libsignal Java/Node/Swift/Android APIs, CryptoKit, CommonCrypto, Security.framework, Android Keystore, Tink and OkHttp included, and since 0.4.1 a TLS version given through a constant or a conditional); cost on hostile input 13/13 linear (`node test/proba-timp.mjs`) | 38/38 (`node test/control-negativ.mjs`); cost 5/5 in this repository (`node test/control-negativ-timp.mjs`; its fourth case compares with version 0.1.0 from the development history and is skipped here) |
| verify-layer | 45/45 with the AIP-23 reference verifier, measured from a fresh clone on 2026-10-18 (`AERE_VERIFY_PROOF=<verify-proof.mjs> node proba-sidecar.mjs`, where verify-proof.mjs is the one served at aere.network/tools with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and `npm install` done there; verify-proof.mjs alone gives 41/45, because the four finality checks need the anchor verifier); without it 40 run, 5 are reported as skipped and the exit code is 2; the cloud adapters 60/60 with the same verifier on 2026-10-18 (`node proba-adaptoare-cloud.mjs`: nine fixtures derived from the vendors' documented CLI and REST examples by `fixturi-cloud/deriva.mjs`; without the verifier 59 run and one is skipped; a real account is unmeasured and the README says so); the Merkle tree 20/20 (`node proba-arbore.mjs`: the reference roots of Certificate Transparency, every proof up to 64 leaves, each attack), measured 2026-09-30 | 37/37 from a fresh clone of this repository on 2026-10-18 (`node control-negativ-sidecar.mjs`: the ten original plantings plus the cloud adapter rules; the case that compares with the development history is skipped here, and is the 38th in the development tree); the tree 14/14 (`node control-negativ-arbore.mjs`) |
| proof-kinds | 28/28 on 2026-10-18 with the same verifier (`AERE_VERIFY_PROOF=... node proba-proof-kinds.mjs`: fifteen kinds, the allowed values of the `secret-access` fields included) | seven negative controls inside the test |
| proof-kinds | 30/30 on 2026-10-19 with the same verifier (`AERE_VERIFY_PROOF=... node proba-proof-kinds.mjs`: sixteen kinds, the allowed values of the `secret-access` fields included) | eight negative controls inside the test |
| readiness | 6/6 (`node proba-adrese-private.mjs`: the private-address rules, and a local listener no scan may touch) | the rate limit and the queue bound are tested where the service runs, not here (its README says so) |
| control-plane | planner 44/44 (2026-10-18: a key agreement or a client-side TLS configured by the application itself is not routed to the TLS gateway; a self-signed trust anchor is blocked, not "automatic through PKI"; test code is LOW with its own scope), command line 10/10, execution 30/30 on real products started locally, remediation 33/33 on real TLS servers (all five server profiles measured end to end on 2026-10-18: nginx, Apache, HAProxy, Node, Go), compliance report 30/30 (with the AIP-23 verifier; test-code findings are counted apart and are not "exposed today"), console 8/8; the console viewer in a real Chromium, phone and desktop, 26/26 (`node proba-consola-web.mjs`, measured 2026-09-30; needs `playwright-core` and a Chromium, otherwise it exits 2) | remediation 7/7, compliance report 3/3 in this repository; viewer 8/8 (`node control-negativ-consola-web.mjs`) |
| identity | 52/52 (`node proba-identity.mjs`, a presentation optionally signed for one exact request (`request`) included, with the AIP-23 verifier for its envelope test; without it that test is reported as skipped and the exit code is 2), compliance 18/18 (`node proba-conformitate.mjs`) and Travel Rule 20/20 (`node proba-travel-rule.mjs`) with the same verifier (without it each reports one test unmeasured and exits 2; 2026-10-18: a missing originator id or nonce is a refusal, found when the verifier was enrolled in the conformance suite), SD-JWT 31/31 (`node proba-sdjwt.mjs`, the RFC 9901 vectors and fixtures derived from @sd-jwt/core 0.23.0 included; since 2026-10-19 the keys of an AERE identity are judged on their ML-DSA-65 half by default, the Ed25519 half only on request, and every verdict says `postQuantum`), SD-JWT VC 11/11 (`node proba-sdjwt-vc.mjs`); interoperability with @sd-jwt/core and jose in both directions 26/26 and 28/28 (`interop/`, with those libraries installed in a folder you name; otherwise reported unmeasured), measured 2026-10-18, before the 2026-10-19 change; the scripts now ask for the Ed25519 half explicitly and have not been re-run since | 57/57 (`node control-negativ-identity.mjs`); compliance 17/17 (`node control-negativ-conformitate.mjs`); Travel Rule 20/20 (`node control-negativ-travel-rule.mjs`); SD-JWT 39/39 (`node control-negativ-sdjwt.mjs`); SD-JWT VC 20/20 (`node control-negativ-sdjwt-vc.mjs`) |
| agents | policy 27/27 with the AIP-23 verifier (without it 25 run, 2 are reported as skipped and the exit code is 2), ledger 59/59 on 2026-10-19 (since then an equivocation proof must be made with the agent's ML-DSA-65 key), approval and revocation 39/39, the chain as witness 26/26 without a network and 7/7 on testnet 28001 on 2026-09-30 (`proba-agent-ancora-testnet.mjs`, needs a funded testnet key and the AIP-23 verifier), command line 23/23 through files and processes only (on Linux and macOS one more test checks the key file mode; not measured here); x402 wallet 25/25, 2-of-2 co-signing 16/16 and payment verifier 14/14 without a network; on the public testnet 28001, 9/9 with the wallet key and 13/13 with the 2-of-2 contract wallet (`agents/x402/proba-x402-testnet.mjs`, `agents/x402/proba-cosign-testnet.mjs`, each needs a funded testnet key); the contract's artifact recompiles byte for byte with solc 0.8.23 (`node x402/recompileaza-contract.mjs --solc <solc>`) | 28/28 (`node control-negativ-aprobare.mjs`); the chain as witness 11/11 (`node control-negativ-ancora.mjs`); x402 30/30 (`node x402/control-negativ-wallet.mjs`); the contract's own tests (7) and their negative control (4/4) run in the Aere Network contracts project, not in this repository |
| agents | policy 27/27 with the AIP-23 verifier (without it 25 run, 2 are reported as skipped and the exit code is 2), ledger 59/59 on 2026-10-19 (since then an equivocation proof must be made with the agent's ML-DSA-65 key), approval and revocation 39/39, the chain as witness 26/26 without a network and 7/7 on testnet 28001 on 2026-09-30 (`proba-agent-ancora-testnet.mjs`, needs a funded testnet key and the AIP-23 verifier), command line 23/23 through files and processes only (on Linux and macOS one more test checks the key file mode; not measured here); the hosted witness 71/71 without a network, with a local notary mock (`proba-martor-agenti.mjs`; the AIP-23 rows need the verifier, without it they are skipped by name and the exit code is 2), and 75/75 on testnet 28001 on 2026-10-19 (`proba-martor-agenti.mjs --testnet`, needs a funded testnet key or the faucet: a checkpoint notarized, the receipt VALID with post-quantum finality, a backdated ledger caught with it; evidence in `agents/dovezi-martor-agenti-28001/`); x402 wallet 25/25, 2-of-2 co-signing 16/16 and payment verifier 14/14 without a network; on the public testnet 28001, 9/9 with the wallet key and 13/13 with the 2-of-2 contract wallet (`agents/x402/proba-x402-testnet.mjs`, `agents/x402/proba-cosign-testnet.mjs`, each needs a funded testnet key); the contract's artifact recompiles byte for byte with solc 0.8.23 (`node x402/recompileaza-contract.mjs --solc <solc>`) | 28/28 (`node control-negativ-aprobare.mjs`); the chain as witness 11/11 (`node control-negativ-ancora.mjs`); the hosted witness 24/24 (`node control-negativ-martor-agenti.mjs`); x402 30/30 (`node x402/control-negativ-wallet.mjs`); the contract's own tests (7) and their negative control (4/4) run in the Aere Network contracts project, not in this repository |
| proof-of-ai | 6/6 with the AIP-23 verifier (`AERE_VERIFY_PROOF=... node proba-proof-of-ai.mjs`); without it the probe reports NEMASURAT and exits 2 | one negative control inside the test (an envelope touched after its hash: INVALID) |
| payments | 23/23 without a chain (`node proba-risc.mjs`: the verdicts under a policy, the reproducible verdict hash, a receipt verified, nine forged ones refused by name, a malformed payment refused by name); 9/9 on testnet 28001 on 2026-10-02 (`AERE_TESTNET_KEY_FILE=... AERE_ETHERS_DIR=... node proba-plata-28001.mjs`: a payment allowed, paid, its receipt notarized and read back with finality PASSED, the public reader VALID; a payment above the limit refused with nothing sent; the fourth in the window refused; a forged receipt and a loosened policy INVALID) | 10/10 (`node proba-risc.mjs --control-negativ`: form, asset, counterparty, rate, per-payment limit, window, linkage, USD arithmetic, verdict reproduction, chain facts, each planted out on a copy and each forged fixture then accepted) |
| market-settlement | 20/20 on testnet 28001 on 2026-10-02 (`AERE_TESTNET_KEY_FILE=... AERE_ETHERS_DIR=... node proba-piata-28001.mjs`: five scenarios on AereComputeMarketV3 deployed from the committed artifact, every refusal by its named custom error, the provider's notarized envelope read back under a post-quantum certified anchor); the judge on the recorded timelines 26/26 without a chain (`node proba-judecata.mjs`: seven honest readings VALID, eighteen forged or malformed ones INVALID for their named reason, the settlement receipt) | 10/10 (`node proba-judecata.mjs --control-negativ`: form, completeness of the timeline, posting, deadline, window, arbiter, payout, result bytes, replay, proof, each planted out on a copy and each forged fixture then accepted) |
@ -64,4 +64,4 @@ interface, command line and data (`definePolicy`, `verifyLedger`, `approve`, ...
## Licence
MIT, see [LICENSE](LICENSE). Files: 967 (pq-gateway 9, pq-kms 10, pq-secrets 6, pq-pki 6, crypto-inventory 73, verify-layer 56, proof-kinds 3, control-plane 27, agents 39, identity 24, proof-of-ai 3, ai-trust 6, verifiable-data 7, market-settlement 9, payments 8, conformance 643, device-identity 11, exposure-report 18, readiness 4).
MIT, see [LICENSE](LICENSE). Files: 975 (pq-gateway 9, pq-kms 10, pq-secrets 6, pq-pki 6, crypto-inventory 73, verify-layer 56, proof-kinds 3, control-plane 27, agents 44, identity 24, proof-of-ai 3, ai-trust 6, verifiable-data 7, market-settlement 9, payments 8, conformance 646, device-identity 11, exposure-report 18, readiness 4).

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,18 @@
// node agent-cli.mjs approve --key human.key.pem --policy p.json --action a.json [--valid 600] [--out ap.json]
// node agent-cli.mjs revoke --key owner.key.pem --policy p.json [--at <unix>] [--reason text] [--out rv.json]
// node agent-cli.mjs verify --ledger l.json --policy p.json [--policy-hash 0x..] [--revocations r1.json,r2.json] [--anchors a.json] [--not-before <unix>]
// a.json: anchors {seq, hash, at}, or receipts of the agents witness (martor-agenti.mjs), which then need --rpc <url> and
// --verify-proof <verify-proof.mjs> (or AERE_VERIFY_PROOF) [--chain 28001] [--max-merge-delay <s>]; without --rpc or the verifier such a
// receipt is not measured (exit 2, unless another piece proves a fault of the ledger)
// [--witness aere-witness:<40 hex>[,...]] the witnesses whose receipts are accepted (a receipt of any other, or a plain anchor:
// not measured; no receipt of one: exit 2); without it any ML-DSA-65 key is a witness of
// itself, and the result says the bound is the chain's alone
// [--witness-url <base>] the full list of heads that witness holds for this agent and policy, signed by it page by page within the
// last 300 s, must be entries of this ledger, and every held head needs a verified receipt (taken from the
// address when --anchors has none; a 429 is waited out as its Retry-After asks, within AERE_WITNESS_WAIT_MAX_S
// seconds, 900 by default); no signed list, or one signed by another witness than --witness: not measured
// verify gathers every piece of evidence first, then gives ONE verdict: 1 if a piece proves a fault of the ledger, else 2 if a
// required piece could not be measured, else 0 (README: "verify is a complete evaluation followed by one verdict")
// node agent-cli.mjs equivocation --a l1.json --b l2.json [--out proof.json]
// node agent-cli.mjs verify-equivocation --proof proof.json
// Iesiri: 0 da (permis, valid, gasit), 1 nu (invalid, negasit, refuzat de verificare), 3 actiune refuzata de politica, 2 folosire gresita.
@ -43,8 +55,35 @@ const acum = () => Math.floor(Date.now() / 1000);
export function main(argv) {
const [cmd, ...rest] = argv;
const get = (f) => { const i = rest.indexOf(f); return i >= 0 ? rest[i + 1] : undefined; };
const lista = (f) => (get(f) ? String(get(f)).split(',').filter(Boolean) : []);
// un steag dat fara valoare (pus ultimul, sau urmat de alt steag) ori cu valoarea goala e folosire gresita (2), nu un steag nedat (revizia B3 a
// treia, R3-03): pana atunci `--witness-url` sau `--policy-hash` puse ultimele, `--revocations ""` (o variabila goala) scoteau in tacere lista
// martorului, pinul politicii sau revocarile, si un registru prins iesea 0. Numai `--reason` are voie sa fie gol.
const GOALE_PERMISE = ['--reason'];
const get = (f) => { const i = rest.indexOf(f); if (i < 0) return undefined; const v = rest[i + 1]; if (!(v !== undefined && !/^--[a-z]/.test(v) && (v !== '' || GOALE_PERMISE.includes(f))) /*PAZA-MARTOR:steag-fara-valoare*/) throw new Folosire(`${f} needs a value`); return v; };
// (un element gol din lista, `a.json,`, ajunge la citesteJson, care il refuza ca folosire gresita)
const lista = (f) => { const v = get(f); return v === undefined ? [] : String(v).split(','); };
// toate aparitiile unui steag repetabil (--witness a --witness b, sau --witness a,b)
const toate = (f) => rest.flatMap((x, i) => (x === f ? (rest[i + 1] === undefined ? [''] : String(rest[i + 1]).split(',')) : []));
// un numar intreg pozitiv; altfel folosire gresita (revizia B3, constatarea 5: `--chain 2800x` dadea NaN si paza lantului cadea DESCHISA)
const intregPozitiv = (f) => { const v = get(f); if (v === undefined) return undefined; if (!/^[1-9]\d{0,14}$/.test(String(v))) throw new Folosire(`${f} must be a positive integer, not "${v}"`); return Number(v); };
// STEAGURILE CUNOSCUTE ale fiecarei comenzi (revizia B3 a patra, R4-05): argumentele vin in perechi steag + valoare, si un argument care nu e un
// steag al comenzii (`--witness-url=<URL>`, o greseala de tastare ca `--ledgr`, o valoare ramasa fara steag) e folosire gresita (2). Pana atunci
// cititorul cauta potrivirea exacta si nu refuza nimic necunoscut, deci `--witness-url=<URL>` sau `--policy-hash=<h>` erau "nedate" si paza lor
// cadea in tacere, cu exit 0. Un steag dat de doua ori e si el folosire gresita, afara de --witness (repetabil). Valorile le judeca `get`.
const STEAGURI = { id: ['--out'], human: ['--out'], policy: ['--spec', '--out'], check: ['--policy', '--action', '--spent'],
record: ['--key', '--policy', '--ledger', '--action', '--approvals', '--provenance'], approve: ['--key', '--policy', '--action', '--valid', '--out'],
revoke: ['--key', '--policy', '--at', '--reason', '--out'],
verify: ['--ledger', '--policy', '--policy-hash', '--revocations', '--anchors', '--not-before', '--rpc', '--verify-proof', '--chain', '--max-merge-delay', '--witness', '--witness-url'],
equivocation: ['--a', '--b', '--out'], 'verify-equivocation': ['--proof'] };
if (Object.hasOwn(STEAGURI, cmd)) {
const vazute = new Set();
for (let i = 0; i < rest.length; i += 2) {
const s = String(rest[i]);
if (!(STEAGURI[cmd].includes(s) /*PAZA-MARTOR:steag-necunoscut*/)) throw new Folosire(`${cmd}: unknown argument ${JSON.stringify(s.slice(0, 60))} (its flags: ${STEAGURI[cmd].join(' ')}; the value follows its flag, never --flag=value)`);
if (!(!vazute.has(s) || s === '--witness') /*PAZA-MARTOR:steag-dublu*/) throw new Folosire(`${cmd}: ${s} is given twice`);
vazute.add(s);
}
}
switch (cmd) {
case 'id':
case 'human': {
@ -110,9 +149,150 @@ export function main(argv) {
const ledger = citesteJson(get('--ledger'), 'ledger');
const j = citesteJson(get('--policy'), 'policy');
const pinned = get('--policy-hash') ?? j.policyHash;
const v = verifyLedger(ledger, { policy: j.policy || j, policyHash: pinned, revocations: lista('--revocations').map((f) => citesteJson(f, 'revocations')),
anchors: get('--anchors') ? citesteJson(get('--anchors'), 'anchors') : [], notBefore: get('--not-before') != null ? Number(get('--not-before')) : null });
console.log(JSON.stringify(v, null, 1)); return v.ok ? 0 : 1;
const revocari = lista('--revocations').map((f) => citesteJson(f, 'revocations'));
const notBefore = get('--not-before') != null ? Number(get('--not-before')) : null;
const date = get('--anchors') ? citesteJson(get('--anchors'), 'anchors') : [];
const toate2 = Array.isArray(date) ? date : [date];
// argumentele martorului se judeca INAINTE de orice: o valoare gresita e folosire gresita (2), niciodata o paza care cade deschisa
const chain = intregPozitiv('--chain'), mergeMax = intregPozitiv('--max-merge-delay');
const martori = toate('--witness');
if (martori.some((w) => !/^aere-witness:[0-9a-f]{40}$/.test(w))) throw new Folosire('--witness must be aere-witness:<40 hex> (repeatable, or comma-separated)');
const urlMartor = get('--witness-url');
if (urlMartor !== undefined && !/^https?:\/\/[^\s]+$/.test(String(urlMartor))) throw new Folosire('--witness-url must be the base URL of a witness (http:// or https://)');
const rpc = get('--rpc'), verifyProof = get('--verify-proof') || process.env.AERE_VERIFY_PROOF;
// bugetul de asteptare pe 429 (R5-06): un numar intreg de secunde; o valoare gresita e folosire gresita, nu o tacere pe valoarea implicita
const asteptareMax = process.env.AERE_WITNESS_WAIT_MAX_S;
if (asteptareMax !== undefined && asteptareMax !== '' && !/^\d{1,6}$/.test(asteptareMax)) throw new Folosire('AERE_WITNESS_WAIT_MAX_S must be a whole number of seconds (0 to 999999)');
// o chitanta fara --rpc (din --anchors sau adusa) e o dovada NEMASURATA cu regula `rpc`, judecata la locul ei, dupa tot ce nu cere lantul (lista,
// capetele tinute, ancorele); pana la a sasea revizie (N1) o chitanta din --anchors fara --rpc iesea aici cu 2 (folosire gresita), INAINTEA listei
// semnate, deci o ramura aratata de lista se ascundea punand in --anchors orice obiect cu forma de chitanta
const ob = (x) => !!x && typeof x === 'object' && !Array.isArray(x); // ca esteChitanta din martor-agenti.mjs (incarcat abia mai jos)
const esteChitanta = (a) => ob(a) && (ob(a.witnessReceipt) || (ob(a.receipt) && ob(a.receipt.witnessReceipt)));
// o chitanta a martorului gazduit (martor-agenti.mjs) e primita ca ancora numai dupa ce ESTE verificata aici: includerea refacuta local si
// momentul capului de jurnal certificat de verificatorul AIP-23 pe lant
const dinMartor = (a) => !!a && typeof a === 'object' && !Array.isArray(a) && ['witnessReceipt', 'receipt', 'promise', 'status'].some((k) => a[k] !== undefined);
const ancoraSimpla = (a) => !!a && typeof a === 'object' && !Array.isArray(a) && !dinMartor(a) && Number.isSafeInteger(a.seq) && a.seq >= 0 && typeof a.hash === 'string' && /^[0-9a-f]{64}$/.test(a.hash) && Number.isSafeInteger(a.at);
const txt = (x) => (typeof x === 'string' ? x.slice(0, 120) : `<${x === null ? 'null' : Array.isArray(x) ? 'array' : typeof x}>`);
return (async () => {
// ================================================================================================================================
// EVALUAREA COMPLETA, UN SINGUR VERDICT (revizia B3 a cincea, 2026-10-19, R5-01..R5-05; scrisa si in README). Pana atunci verificarea
// decidea PE DRUM, iesind cu 2 sau 1 la primul semn, in ordinea citirii, iar ordinea o alegea agentul: un cap proaspat in asteptare, un
// {"status":"pending"} in --anchors sau o copie veche a unei chitante ascundeau o antedatare DOVEDITA (R5-01), iar o chitanta cazuta din
// vina notarului dadea 1 predata si 2 adusa (R5-03). Acum:
// 1. Se ADUNA toate dovezile: fiecare intrare din --anchors (orice forma), lista semnata a martorului, fiecare cap tinut, chitanta
// adusa de la adresa pentru fiecare cap tinut fara o chitanta VERIFICATA in --anchors. Nimic nu iese din functie in timpul adunarii.
// 2. Fiecare dovada primeste o CLASA: VERIFICATA (tine, da o margine), VINA-REGISTRULUI (dovedita: un cap tinut de martor care nu e
// intrare a registrului, o echivocare pastrata, o chitanta a martorului fixat legata de alt registru, o chitanta predata de agent cu
// semnatura martorului pe care il numeste INVALIDA, o intrare antedatata fata de o margine din dovezi VERIFICATE), NEMASURATA
// (orice altceva: in asteptare, forma gresita, notarul in urma, finalitatea data de RPC-ul verificatorului, o lista semnata de alt
// martor decat cel fixat, o ancora simpla sub un martor fixat, o adresa care strica raspunsul).
// 3. VERDICTUL: FAILED daca exista o VINA-REGISTRULUI; altfel NOT MEASURED daca o dovada CERUTA e NEMASURATA; altfel OK. Deci o vina
// dovedita nu poate fi ascunsa de un NEMASURAT, oricat de devreme vine, iar o pana a martorului nu face dintr-un registru cinstit un
// FAILED. O dovada e CERUTA afara de o chitanta (sau un raspuns in asteptare) din --anchors al carei cap are deja o chitanta VERIFICATA.
// REGULA (c): nicio forma de raspuns nu arunca; tipurile se judeca inainte de folosire (martor-agenti.mjs: sir(), chitanta-tipuri).
// ================================================================================================================================
// modulul martorului se incarca numai cand o dovada sau un steag il cere: un registru cu ancore simple (sau fara) se verifica si fara el
const M = urlMartor !== undefined || martori.length > 0 || toate2.some(dinMartor) ? await import('./martor-agenti.mjs') : null;
const dovezi = [];
const adauga = (d) => { dovezi.push({ ceruta: true, ...d }); };
let fixati = martori.length ? martori : null;
const fixare = !!fixati || urlMartor !== undefined;
// buget comun de asteptare pe 429 (Retry-After), pentru lista si chitantele aduse (R5-06)
const buget = { ramasS: asteptareMax ? Number(asteptareMax) : 900, asteptatS: 0 };
const cere = M ? M.cereRabdator(buget) : null;
// 1a. lista semnata a capetelor (sub --witness-url)
let tinute = null;
if (urlMartor !== undefined) {
const c = await M.citesteCapeteMartorului(urlMartor, ledger.agentId, ledger.session, { cere });
if (!c.ok) adauga({ sursa: 'list', clasa: 'NEMASURATA', detail: `the witness at --witness-url did not give a list of heads it signed: ${c.motiv}` });
else if (!(!fixati || fixati.includes(c.witness)) /*PAZA-MARTOR:martor-url*/) adauga({ sursa: 'list', clasa: 'NEMASURATA', rule: 'martor', detail: `the list at --witness-url is signed by ${c.witness}, not by a pinned witness (${fixati.join(', ')}): not the testimony of the pinned witness (another address, or a server without its key)` });
else {
fixati = fixati || [c.witness];
const vc = M.verificaCapeteMartorului(ledger, c);
for (const x of vc.randuri) adauga({ sursa: `list of ${c.witness} (signed at ${c.at})`, clasa: x.pass === true ? 'VERIFICATA' : x.pass === false ? 'VINA' : 'NEMASURATA', rule: x.rule, seq: x.seq, detail: x.detail });
tinute = c.heads;
}
}
// o chitanta, din --anchors (`agent`) sau adusa de la adresa (`adusa`), judecata local si apoi pe lant; fara exceptie pe nicio forma
const idCap = (a) => { const p = M.chitantaDin(a); const w = p && typeof p === 'object' ? p.witnessReceipt : null; const h = w && typeof w === 'object' ? w.head : null; return h && typeof h === 'object' && typeof h.statementHash === 'string' ? h.statementHash : null; } /*PAZA-MARTOR:chitanta-id-sigur*/;
const areVerificator = !!verifyProof && fs.existsSync(verifyProof);
const o = { verifyProof, rpc, chain: chain ?? null, mergeMax, martori: null, verdicte: new Map() };
const evalueaza = async (x, sursa) => {
const plic = M.chitantaDin(x);
const l = M.verificaChitanta(plic, { ledger, martori: fixati });
if (!l.ok) {
// vina registrului: o chitanta a martorului fixat legata de alt registru (alt agent, alta politica, alta ramura); si, numai pentru una PREDATA
// de agent, o semnatura INVALIDA a martorului pe care il numeste (cand e fixat, sau cand nu e fixat niciunul)
const vp = M.verificaPunct(plic);
const numit = vp.rule === 'martor' || vp.rule === 'punct-semnatura' ? plic.statement.witness : null;
const falsificata = sursa === 'agent' && numit !== null && (!fixati || fixati.includes(numit)) /*PAZA-MARTOR:chitanta-falsificata*/;
const vina = l.rule === 'registru' /*PAZA-MARTOR:chitanta-alt-registru*/ || falsificata;
return { chitanta: true, clasa: vina ? 'VINA' : 'NEMASURATA', rule: l.rule, detail: l.error };
}
if (!rpc /*PAZA-MARTOR:fara-rpc*/) return { chitanta: true, seq: l.head.seq, clasa: 'NEMASURATA', rule: 'rpc', detail: 'a witness receipt needs --rpc <url>: the chain is read, not trusted from the receipt' };
if (!areVerificator) return { chitanta: true, seq: l.head.seq, clasa: 'NEMASURATA', rule: 'lant', detail: 'a witness receipt needs the AIP-23 reference verifier (--verify-proof <verify-proof.mjs> or AERE_VERIFY_PROOF)' };
// pe lant: nenotarizata, intarziata, cu finalitatea refuzata de RPC-ul ales de verificator: vina martorului, a notarului sau a RPC-ului, nu a
// registrului, pe ambele drumuri (R5-03: pana atunci o chitanta predata de agent dadea aici FAILED, iar aceeasi, adusa, NEMASURAT)
const r = await M.ancoreDinChitante(ledger, [plic], { ...o, martori: fixati });
const rd = r.randuri[0] || {};
if (r.stare === 'OK' && r.anchors.length === 1) return { chitanta: true, seq: l.head.seq, clasa: 'VERIFICATA', ancora: r.anchors[0], detail: rd.detail };
return { chitanta: true, seq: l.head.seq, clasa: 'NEMASURATA' /*PAZA-MARTOR:lant-nemasurat*/, rule: rd.rule || 'lant', detail: rd.detail || 'not judged' };
};
// 1b. fiecare intrare din --anchors, oricare ar fi forma ei
const fazaAnchors = async () => { for (const [i, a] of toate2.entries()) {
const sursa = `anchors[${i}]`;
if (esteChitanta(a)) { const e = await evalueaza(a, 'agent'); adauga({ sursa, id: idCap(a), ...e }); continue; }
if (dinMartor(a)) { adauga({ sursa, id: typeof a.id === 'string' && /^0x[0-9a-f]{64}$/.test(a.id) ? a.id : null, clasa: 'NEMASURATA', rule: 'fara-chitanta', detail: `an answer of the witness that carries no receipt yet (status ${txt(a.status)}): ask the witness again after its next checkpoint` }); continue; }
if (ancoraSimpla(a)) {
if (fixare /*PAZA-MARTOR:martor-simple*/) adauga({ sursa, seq: a.seq, clasa: 'NEMASURATA', rule: 'martor', detail: 'a plain anchor {seq, hash, at} under a pinned witness is not the testimony of the witness (anchors of your own run, such as agent-ancora.mjs anchors, are judged in a run without --witness and --witness-url)' });
else adauga({ sursa, seq: a.seq, clasa: 'VERIFICATA', ancora: { seq: a.seq, hash: a.hash, at: a.at }, detail: `a plain anchor at seq ${a.seq}, taken as the verifier's own (no witness pinned)` });
continue;
}
adauga({ sursa, clasa: 'NEMASURATA', rule: 'forma', detail: `not a piece of evidence: neither a receipt, nor an answer of the witness, nor an anchor {seq, hash, at} (${txt(a)})` });
} };
// 1c. fiecare cap tinut de martor, cu o chitanta VERIFICATA: a agentului, altfel adusa de la adresa (oricare cap, nu doar ultimul; o chitanta a
// agentului cu acelasi id care nu tine nu tine locul celei verificate, R5-01)
const descrie = (r) => (!r.status ? `the witness did not answer (${txt(r.eroare)})` : r.status === 429 ? `the witness answered HTTP 429 (Retry-After ${r.retryAfter === null ? 'none' : r.retryAfter + ' s'}; ${buget.asteptatS} s already waited of a budget of ${buget.asteptatS + buget.ramasS} s, AERE_WITNESS_WAIT_MAX_S)`
: r.status !== 200 ? `the witness answered HTTP ${r.status}` : !(r.j && typeof r.j === 'object' && !Array.isArray(r.j)) ? 'the answer is not the state of a head (not JSON)'
: !M.esteChitanta(r.j) ? (typeof r.j.status === 'string' && r.j.status !== 'notarized' ? `the witness has it ${txt(r.j.status)}, not notarized yet (ask again after its checkpoint is notarized)` : 'the answer carries no receipt')
: idCap(r.j) === null ? 'the receipt in the answer has no head' : 'the answer is the receipt of another head');
const fazaCapete = async () => { for (const h of tinute || [] /*PAZA-MARTOR:martor-fiecare-cap*/) {
if (dovezi.some((d) => d.clasa === 'VERIFICATA' && d.id === h.id) /*PAZA-MARTOR:martor-chitanta-ceruta*/) continue;
const sursa = `seq ${h.seq}, taken from --witness-url`;
const r = await M.stareaCapuluiLa(urlMartor, h.id, { cere });
if (!(r.status === 200 && M.esteChitanta(r.j) && idCap(r.j) === h.id)) { adauga({ sursa, seq: h.seq, id: h.id, clasa: 'NEMASURATA', rule: 'adusa', detail: `the witness holds seq ${h.seq} and no verified receipt of it was in --anchors; ${descrie(r)}: the entries after seq ${h.seq} are not bounded by the witness` }); continue; }
const e = await evalueaza(r.j, 'adusa');
adauga({ sursa, seq: h.seq, id: h.id, ...e, detail: e.clasa === 'NEMASURATA' ? `${e.detail}: the entries after seq ${h.seq} are not bounded by the witness` : e.detail });
} };
// adunarea: --anchors, apoi capetele tinute (o chitanta VERIFICATA a agentului scuteste aducerea); ordinea nu schimba verdictul, numai cate se aduc
await fazaAnchors(); await fazaCapete();
// 1d. --witness cere o legatura SEMNATA de martorul fixat: fara nicio chitanta de judecat, marginea martorului nu e masurata
if (!(!martori.length || dovezi.some((d) => d.chitanta) || (tinute && tinute.length)) /*PAZA-MARTOR:martor-fara-chitanta*/) adauga({ sursa: 'witness', clasa: 'NEMASURATA', rule: 'martor', detail: '--witness was given, but there is no receipt of a pinned witness: the bound of the witness was not measured' });
// o chitanta (sau un raspuns in asteptare) din --anchors al carei cap are o chitanta VERIFICATA nu mai e ceruta (o copie veche)
for (const d of dovezi) if (d.clasa === 'NEMASURATA' && d.id && dovezi.some((x) => x !== d && x.clasa === 'VERIFICATA' && x.id === d.id) /*PAZA-MARTOR:copie-veche*/) d.ceruta = false;
// 2. registrul, judecat cu ancorele VERIFICATE (o antedatare fata de ele e vina registrului)
const ancore = dovezi.filter((d) => d.clasa === 'VERIFICATA' && d.ancora).map((d) => d.ancora).sort((a, b) => a.seq - b.seq);
const v = verifyLedger(ledger, { policy: j.policy || j, policyHash: pinned, revocations: revocari, anchors: ancore, notBefore });
if (!v.ok) adauga({ sursa: 'ledger', clasa: 'VINA', rule: v.rule, detail: v.error });
// 3. UN SINGUR verdict, dupa ce s-a adunat tot
const vina = dovezi.filter((d) => d.clasa === 'VINA'), nemasurate = dovezi.filter((d) => d.clasa === 'NEMASURATA' && d.ceruta);
const verdict = vina.length ? 'FAILED' : nemasurate.length ? 'NOT MEASURED' : 'OK' /*PAZA-MARTOR:verdict-vina-intai*/;
for (const d of dovezi) console.error(`${d.clasa === 'VERIFICATA' ? 'ok' : d.clasa === 'VINA' ? 'FAIL' : d.ceruta ? '--' : '..'} ${d.sursa}: ${d.rule ? '[' + d.rule + '] ' : ''}${d.detail}${d.ceruta ? '' : ' (superseded by a verified receipt of the same head)'}`);
const chit = ancore.filter((a) => a.witness);
if (chit.length) {
v.witnessReceipts = chit.map((a) => ({ seq: a.seq, witness: a.witness, pinned: a.witnessPinned, chainLowerBound: a.chainLowerBound }));
if (v.time && chit.some((a) => !a.witnessPinned)) v.time.lowerBound = 'witnessed by an UNPINNED witness: the chain alone bounds each head to no earlier than its chainLowerBound (F minus the merge delay minus 600 s); pass --witness to pin the witness';
}
if (!fixati && chit.length) console.error('NOTE: no witness was pinned (--witness or --witness-url): each receipt above bounds its head by the chain alone, not by the witness clock');
if (tinute && !tinute.length && verdict === 'OK') console.error('NOTE: the witness holds no head of this ledger: the list was checked, but the time of the entries is not bounded by the witness');
if (buget.asteptatS) console.error(`NOTE: the witness rate-limited this verification (HTTP 429): ${buget.asteptatS} s waited, as its Retry-After asked, of a budget of ${buget.asteptatS + buget.ramasS} s (AERE_WITNESS_WAIT_MAX_S)`);
v.verdict = verdict; v.ok = verdict === 'OK' ? true : verdict === 'FAILED' ? false : null;
v.evidence = dovezi.map((d) => ({ source: d.sursa, class: d.clasa === 'VINA' ? 'LEDGER-FAULT' : d.clasa === 'VERIFICATA' ? 'VERIFIED' : 'NOT-MEASURED', required: d.ceruta, ...(d.seq !== undefined ? { seq: d.seq } : {}), ...(d.rule ? { rule: d.rule } : {}) }));
if (verdict === 'FAILED') console.error(`FAILED: ${vina.length} piece(s) of evidence show a fault of the ledger (${[...new Set(vina.map((d) => d.rule || 'ledger'))].join(', ')})`);
if (verdict === 'NOT MEASURED') console.error(`NOT MEASURED: ${nemasurate.length} required piece(s) of evidence could not be verified, and none shows a fault of the ledger (${[...new Set(nemasurate.map((d) => d.rule || 'list'))].join(', ')})`);
console.log(JSON.stringify(v, null, 1));
return verdict === 'OK' ? 0 : verdict === 'FAILED' ? 1 : 2;
})();
}
case 'equivocation': {
const p = findEquivocation(citesteJson(get('--a'), 'a'), citesteJson(get('--b'), 'b'));
@ -130,8 +310,7 @@ export function main(argv) {
}
if (import.meta.url === pathToFileURL(process.argv[1] || '').href) {
let cod;
try { cod = main(process.argv.slice(2)); }
catch (e) { console.error(`agent: ${e.message}`); cod = e instanceof Folosire ? 2 : 1; }
process.exitCode = cod;
// main intoarce un cod, sau (verify cu o chitanta a martorului, care citeste lantul) o promisiune de cod
Promise.resolve().then(() => main(process.argv.slice(2)))
.then((cod) => { process.exitCode = cod; }, (e) => { console.error(`agent: ${e.message}`); process.exitCode = e instanceof Folosire ? 2 : 1; });
}

View File

@ -0,0 +1,325 @@
// Controlul negativ al martorului gazduit al agentilor (martor-agenti.mjs, martor-agenti-serviciu.mjs, proba-martor-agenti.mjs; B3, 2026-10-19):
// fiecare paznic scos intr-o COPIE a componentei trebuie sa inroseasca proba NUMITA, cu proba chiar rulata (rezumatul ei exista); pe copia
// neatinsa, proba e verde. Trei stari, ca la celelalte controale ale agentilor: ROSU cum trebuia (randul numit pica), CONTROL CAZUT (randul a
// ramas verde) si STRICAT (tiparul nu apare exact o data, sau proba nu ajunge la rezumat), iar ultimele doua se numara esecuri.
// Plantarile se ancoreaza pe partea STABILA a randului (conditia cu markerul ei PAZA-MARTOR), nu pe tot randul, si toate ruleaza (JavaScript:
// o conditie inlocuita cu `false`), deci un rosu e al probei, nu al unui fisier care nu se mai incarca.
// ACOPERIREA (2026-10-19, revizia adversariala B3, constatarea 6): pana la reparatie controlul planta 24 de paznici si README-ul spunea "each
// guard", desi primire (de doua ori), martor, lant (pe lant si pe nivelul semnaturii), promisiune, sesiune, integritate si punct-integritate nu
// erau plantati. Acum FIECARE marker PAZA-MARTOR din martor-agenti.mjs si martor-agenti-serviciu.mjs trebuie sa fie in tiparul unei plantari, sau
// pe lista EXCEPTII cu motivul scris; un marker neacoperit e un ESEC al controlului. Garda isi are si ea martorul: cu o plantare scoasa, trebuie
// sa numeasca exact markerul ei. Pe langa markeri, plantarile pazesc si ordinea si forma reparatiilor din revizie (cota inainte de "deja vazut",
// scrierea pana la ultimul octet, taierea inapoi, paginarea, cheia /64, linia de comanda, vocabularul felului in proof-kinds, usa gateway-ului).
// A DOUA REVIZIE (2026-10-19, 15 constatari R2-01..R2-15): fiecare paznic nou are plantarea lui, tinta randul lui 10.R2-<nn> / 10.P2-<nn>; garda de
// acoperire priveste acum si agent-cli.mjs (R2-15: potrivirea --witness / --witness-url si ancorele simple sub un martor fixat nu aveau nici
// plantare, nici rand), iar paznicul radacinii de la deschidere e impartit in doua (marimea, radacina), fiecare cu randul lui.
// A TREIA REVIZIE (2026-10-19, 5 constatari R3-01..R3-05): fiecare paznic nou are plantarea lui, tinta randul lui 11.R3-<nn> / 11.P3-<nn>.
// A PATRA REVIZIE (2026-10-19, 6 constatari R4-01..R4-06): regula de proiectare a lui --witness-url (agent-cli.mjs, README) si paznicii ei, fiecare
// cu plantarea lui si tinta 12.R4-<nn> / 12.P4-<nn>; paznicul `martor-adusa-vina` are doua plantari, una pe fiecare sens (aduse FAILED / ramura 2).
// A CINCEA REVIZIE (2026-10-19, 6 constatari R5-01..R5-06): verify e o EVALUARE COMPLETA cu un singur verdict (agent-cli.mjs, README); paznicii ei
// (ordinea claselor in verdict, clasa fiecarei chitante, copia veche, tipurile, asteptarea pe 429) au plantarile lor, tinta 13.R5-<nn> / 13.P5-<nn>, iar
// plantarile NEUTRE rastoarna ordinea in care vin dovezile si cer proba verde intreaga. Plantarea scurtaturii `--witness ocolit` a disparut cu scurtatura.
// A SASEA REVIZIE (2026-10-19, verificarea tintita a reparatiilor R5, N1..N3): o chitanta fara --rpc ca dovada NEMASURATA [rpc] (doua plantari, una pe
// fiecare sens), `receipt` care scrie numai chitanta verificata a capului cerut (id, registru, martorul fixat, fiecare cu plantarea lui) si tipurile pe
// drumul "nimic nou" al lui submit; tinta 14.N<n>.
// Copia pastreaza asezarea relativa (dosarul componentei si surorile ei, `../aere-verify-layer` sau `../verify-layer`, `../proof-kinds`), derivata
// din importurile modulului, in arborele de dezvoltare si in copia publicata. Verificatorul AIP-23 si gateway-ul (daca sunt) se dau probei
// din locul lor, neatinse: AERE_VERIFY_PROOF, AERE_GATEWAY; plantarea usii gateway-ului lucreaza pe o copie a dosarului cloud-gateway.
// node control-negativ-martor-agenti.mjs iesire 0 = martorul verde, acoperirea intreaga si toate plantarile rosii pe randul lor
import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path';
import { spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const MA = 'martor-agenti.mjs', MS = 'martor-agenti-serviciu.mjs', CLI = 'agent-cli.mjs', PKF = '../proof-kinds/proof-kinds.mjs', GWF = 'gateway';
const PLANTARI = [
// [nume, fisier, tipar, inlocuire, randul probei (inceputul numelui lui)]
// ---- cererea (verificaCapSemnat)
['plicul neobiect nejudecat (forma)', MA, 'if (!(obiect(env) && obiect(env.statement) /*PAZA-MARTOR:forma*/)) return', 'if (false) return', '2. CONTROL ritm pe client'],
['forma capului nejudecata', MA, 'if (!(formaBuna /*PAZA-MARTOR:forma*/)) return', 'if (false) return', '1. CONTROL: forma capului'],
['continutul registrului primit in cerere', MA, 'if (!(inPlus.length === 0 /*PAZA-MARTOR:continut*/)) return', 'if (false) return', '1. CONTROL: intrarile registrului langa cap'],
['sesiunea nederivata din agent si politica', MA, 'if (!(st.session === sessionId(st.agentId, st.policyHash) /*PAZA-MARTOR:sesiune*/)) return', 'if (false) return', '1. CONTROL: o sesiune care nu e cea derivata'],
['integritatea capului nejudecata', MA, 'if (!(sha(text) === env.statementHash /*PAZA-MARTOR:integritate*/)) return', 'if (false) return', '1. CONTROL: declaratia schimbata fara hash refacut'],
['cheia agentului poate fi oricare', MA, 'if (!(!!pub && pub.asymmetricKeyType === ALG /*PAZA-MARTOR:cheie*/)) return', 'if (!(!!pub /*PAZA-MARTOR:cheie*/)) return', '1. CONTROL: un cap semnat cu o cheie Ed25519'],
['agentId nederivat din cheie', MA, 'if (!(agentIdFromKey(pub) === st.agentId /*PAZA-MARTOR:identitate*/)) return', 'if (false) return', '1. CONTROL: cheia lui A semneaza un cap cu id-ul lui B'],
['semnatura capului nejudecata', MA, 'if (!(verificaSemnaturaText(text, pub, env.signature.signature) /*PAZA-MARTOR:semnatura*/)) return', 'if (false) return', '1. CONTROL: declaratia schimbata si hash-ul refacut'],
['momentul cererii nejudecat fata de ceasul martorului', MA, 'if (!(Math.abs(t - Number(acumS)) <= toleranta /*PAZA-MARTOR:timp*/)) return', 'if (false) return', '1. CONTROL: un cap declarat cu 400 s'],
// ---- capul de jurnal si chitanta (verificaPunct, verificaChitanta)
['cheia care semneaza capul de jurnal nu trebuie sa fie a martorului numit', MA, 'if (!(!!pub && pub.asymmetricKeyType === ALG && witnessIdFromKey(pub) === witnessId /*PAZA-MARTOR:martor*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal semnat de alta cheie'],
['semnatura martorului pe capul de jurnal nejudecata', MA, 'if (!(verificaSemnaturaText(JSON.stringify(env.statement), pub, s.signature) /*PAZA-MARTOR:punct-semnatura*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal schimbat (hash refacut'],
['forma capului de jurnal nejudecata', MA, 'if (!(obiect(env) && env.kind === CHECKPOINT_KIND + \'-attestation\' && formaPunctului(st) /*PAZA-MARTOR:punct-forma*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal cu marimea 0'],
['integritatea capului de jurnal nejudecata', MA, 'if (!(H32.test(sir(env.statementHash)) && sha(JSON.stringify(st)) === env.statementHash /*PAZA-MARTOR:punct-integritate*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal schimbat fara hash refacut'],
['forma chitantei nejudecata', MA, '&& Array.isArray(w.path) /*PAZA-MARTOR:chitanta-forma*/)) return', '&& Array.isArray(w.path) || true)) return', '3. CONTROL chitanta: chitanta fara leafHash'],
['martorul FIXAT de verificator nejudecat', MA, 'if (!(!fixati || fixati.includes(st.witness) /*PAZA-MARTOR:martor*/)) return', 'if (false) return', '9.R1 un martor COERENT'],
['frunza nelegata de momentul primirii', MA, 'if (!(frunzaMartor(w.head.statementHash, w.receivedAtMs) === w.leafHash /*PAZA-MARTOR:frunza*/)) return', 'if (false) return', '3. CONTROL chitanta: alt moment de primire'],
['dovada de includere falsa primita', MA, 'if (!(w.treeSize === st.treeSize && verifyInclusion({ leafHash: w.leafHash, index: w.leafIndex, treeSize: w.treeSize, path: w.path, rootHash: st.root }) /*PAZA-MARTOR:includere*/)) return', 'if (false) return', '3. CONTROL chitanta: un element al drumului schimbat'],
['regula de primire a martorului nejudecata in chitanta', MA, 'if (!(Math.abs(C - R) <= toleranta /*PAZA-MARTOR:primire*/)) return', 'if (false) return', '3. CONTROL: o chitanta a unui martor care a scris in frunza'],
['promisiunea nejudecata', MA, 'verificaSemnaturaMartor(pr, st.witness).ok /*PAZA-MARTOR:promisiune*/)) return', 'verificaSemnaturaMartor(pr, st.witness).ok || true)) return', '3. CONTROL chitanta: promisiunea cu alt moment'],
['capul martorizat nu mai trebuie sa fie al registrului verificat', MA, '&& (!h.previous || (!!pe && pe.hash === h.previous.hash)) /*PAZA-MARTOR:registru*/)) return', '&& true) && false) return', '3. CONTROL chitanta: judecata pe alta ramura'],
// ---- ancorele (ancoreDinChitante)
['lantul notarizarii nu mai trebuie sa fie al capului de jurnal si cel cerut', MA, 'if (!(lantNotar === plic.statement.chainId && (!chain || chain === plic.statement.chainId) /*PAZA-MARTOR:lant*/)) {', 'if (false) {', '4. CONTROL: chitanta pe alta ramura'],
['plicul judecat de verificatorul AIP-23 nu mai trebuie sa fie acesta', MA, "if (!(String(v.statementHash || '').toLowerCase() === plic.statementHash /*PAZA-MARTOR:lant*/)) {", 'if (false) {', '4. CONTROL: verificatorul a judecat alt statementHash'],
['semnatura martorului la verificatorul AIP-23 nu mai trebuie sa fie PASSED', MA, "if (!(!!sg && sg.state === 'PASSED' /*PAZA-MARTOR:lant*/)) {", 'if (false) {', '4. CONTROL: semnatura martorului FAILED'],
['finalitatea nu mai trebuie sa fie PASSED', MA, "if (!(fs2.stare === 'PASSED' /*PAZA-MARTOR:lant*/)) {", 'if (false) {', '4. CONTROL: finalitate in asteptare'],
['primirea dupa momentul lantului primita', MA, 'if (!(l.receivedAt <= F + toleranta /*PAZA-MARTOR:primire*/)) {', 'if (false) {', '4. CONTROL: lantul a vazut capul de jurnal INAINTE'],
['intarzierea maxima nejudecata', MA, 'if (!(F - l.receivedAt <= mmd + toleranta /*PAZA-MARTOR:intarziere*/)) {', 'if (false) {', '4. CONTROL: capul de jurnal vazut de lant la 4000 s'],
['radacina neverificata pe lant (momentul luat din notarizarea scrisa in chitanta)', MA, 'const fs2 = firstSeenFromVerdict(v);', "const fs2 = { stare: 'PASSED', at: Number(n.firstSeen) };", '4. CONTROL: finalitate FAILED'],
['lantul cerut nevalid primit de biblioteca', MA, 'if (!(chain == null || (Number.isSafeInteger(chain) && chain > 0))) throw', 'if (false) throw', '9.R5 agent-cli verify --chain 2800x'],
// ---- lista intreaga a capetelor martorului (verificaCapeteMartorului)
// (tinta mutata la a treia revizie: de la R3-04 chitanta ultimului cap prinde si ea ramura din 9.R2, deci randul acela nu mai deosebea paznicul)
['un cap martorizat care nu e in registrul aratat primit', MA, 'if (!(!!e && e.hash === h.hash /*PAZA-MARTOR:registru*/)) {', 'if (false) {', '11.P3-04b CONTROL'],
['o dovada de echivocare tinuta de martor ignorata', MA, 'if (!(eq.length === 0 /*PAZA-MARTOR:echivocare*/)) {', 'if (false) {', '9.R2 CONTROL: --witness-url'],
// ---- deschiderea starii
['un rand de frunza stricat in mijloc taiat in tacere', MA, 'if (!(bun /*PAZA-MARTOR:frunze-mijloc*/)) throw', 'if (false) throw', '9.R16 o stare cu un rand de frunza stricat'],
['randul taiat la coada ramas in jurnal', MA, "if (coada !== '' /*PAZA-MARTOR:coada*/) {", 'if (false) {', '3. CONTROL: un rand de frunza taiat la coada'],
['un cap de jurnal peste frunze pierdute primit la deschidere (marimea)', MA, 'if (!(p.treeSize <= ciob.frunze.length /*PAZA-MARTOR:radacina-marime*/)) throw', 'if (false) throw', '9.R16 o stare cu un rand de frunza stricat'],
// revizia B3 a doua, R2-15: jumatatea "radacina" are acum plantarea ei si randul ei (o frunza din mijloc inlocuita de alt rand valid)
['radacina capului de jurnal nejudecata la deschidere', MA, 'if (!(ciob.arbore.root(p.treeSize) === p.root /*PAZA-MARTOR:radacina*/)) throw', 'if (false) throw', '10.R2-15 o stare cu o frunza din mijloc'],
['cioburile vechi pastrate (memoria si discul cresc)', MA, 'for (const j of [...cioburi.keys()]) if (j <= k - CIOBURI_PASTRATE) scoateCiob(j);', '', '9.R3/R12 cioburile'],
// ---- primirea (primeste, echivoca)
['mai multe dovezi pastrate pe agent si politica', MA, '!z.eq /*PAZA-MARTOR:echivocare-una*/', 'true', '2. CONTROL: zece echivocari'],
['dovezile pastrate fara plafon total', MA, 'echivocari.size < maxEchivocari /*PAZA-MARTOR:echivocare-plafon*/', 'true', '9.R9 un agent cu 12 politici'],
['pastrarea unei dovezi noi nu consuma cota agentului', MA, 'if (limitaAgent && limitaAgent(proof.agentId) /*PAZA-MARTOR:ritm*/) return', 'if (false) return', '9.P11 CONTROL (paznicul nou al lui R11)'],
['echivocarea nepastrata (fara index)', MA, 'echivocari.set(pid, q) /*PAZA-MARTOR:echivocare*/;', '', '2. GET heads?session'],
['echivocarea la aceeasi pozitie nedetectata', MA, 'if (laSeq && !(laSeq.e === st.hash) /*PAZA-MARTOR:echivocare*/) return', 'if (false) return', '2. ECHIVOCARE: alta ramura a lui A'],
['echivocarea prin predecesor nedetectata', MA, 'if (laPrev && !(laPrev.e === st.previous.hash) /*PAZA-MARTOR:echivocare*/) return', 'if (false) return', '2. ECHIVOCARE prin predecesor'],
['"deja vazut" judecat inaintea predecesorului contrazis', MA, 'const laPrev = st.previous ? gaseste(lista, st.previous.seq) : null;', 'const laPrev = st.previous ? gaseste(lista, st.previous.seq) : null; if (laSeq && laSeq.e === st.hash) return stareaCapului(laSeq.h);', '9.R4 un cap la un seq deja tinut'],
['un cap mai VECHI primit', MA, 'if (!(!ultim || st.seq > ultim.q) /*PAZA-MARTOR:vechi*/) return', 'if (false) return', '2. CONTROL: un cap mai VECHI'],
['consistenta capetelor nejudecata (succesorul)', MA, 'if (!(prevBun /*PAZA-MARTOR:succesor*/)) return', 'if (false) return', '2. CONTROL: un cap care nu numeste ca predecesor'],
['limita de ritm pe agent scoasa', MA, 'if (limitaAgent && limitaAgent(st.agentId) /*PAZA-MARTOR:ritm*/) return', 'if (false) return', '2. CONTROL ritm pe agent'],
['limita pe agent judecata pe corpul neautentificat', MA, 'const v = verificaCapSemnat(env, { acumS: acumMs / 1000, toleranta });', "if (limitaAgent && env && env.statement && limitaAgent(String(env.statement.agentId))) return { cod: 429, corp: { error: 'rate_limited' } }; const v = verificaCapSemnat(env, { acumS: acumMs / 1000, toleranta });", '2. CONTROL: 14 cereri cu semnatura rea'],
['cota pe agent consumata de un cap deja vazut (retrimiterea capului public)', MA, 'if (dupaCap.has(id)) return stareaCapului(id);', "if (limitaAgent && limitaAgent(st.agentId)) return { cod: 429, corp: { error: 'rate_limited' } }; if (dupaCap.has(id)) return stareaCapului(id);", '9.R11 un strain retrimite'],
['bugetul jurnalului pe ora scos', MA, 'if (bugetPlin /*PAZA-MARTOR:buget*/) return', 'if (false) return', '9.R12 bugetul jurnalului'],
['ciobul plin nerotit', MA, 'if (!(cur.frunze.length < maxFrunze /*PAZA-MARTOR:plin*/)) roteste();', 'if (false) roteste();', '9.R3/R12 cioburile'],
// ---- scrierile durabile, paginarea, erorile publice
['o scriere scurta neterminata (un singur writeSync)', MA, 'off += n;', 'off = buf.length;', '9.R16 fiecare fs.writeSync'],
['o scriere cazuta lasata pe disc (fara taiere inapoi)', MA, 'try { fs.ftruncateSync(fd, inainte); fs.fsyncSync(fd); } catch { try { fs.truncateSync(f, inainte); } catch { e.taiereEsuata = true; } }', '', '9.R16 o scriere a frunzei care cade'],
['mesajul notarizarii cazute public in lastError', MA, "else p.lastError = rez && typeof rez.cod === 'string' && /^[a-z_]{1,40}$/.test(rez.cod) ? rez.cod : 'notarization_failed';", 'else p.lastError = String(rez && rez.motiv);', '9.R18 o notarizare cazuta'],
['GET heads nepaginat', MA, 'const a = primulDeLa(l, from); const pagina = l.slice(a, a + limit);', 'const a = 0; const pagina = l;', '9.R10 GET heads?session'],
['submit iese 1 cand martorul tine deja intrarea ceruta', MA, 'if (ultim.seq === s && e && e.hash === ultim.hash) {', 'if (false) {', '9.R7 submit fara nimic nou'],
// ---- serviciul
['limita de ritm pe client scoasa', MS, 'if (peClient(client) /*PAZA-MARTOR:ritm*/) return', 'if (false) return', '2. CONTROL ritm pe client'],
['plafonul de chei al limitatorului scos', MS, 'if (!(m.size < maxChei /*PAZA-MARTOR:ritm-plafon*/)) {', 'if (false) {', '9.R13 limitatorul'],
['cheile expirate ale limitatorului necuratate', MS, 'if (m.size >= maxChei && acum - ultimaCuratare >= 1000) curata(acum);', '', '9.R13 limitatorul'],
['cheia unui client IPv6 pe adresa intreaga (nu pe /64)', MS, 'return tot.slice(0, 4).map(', 'return tot.slice(0, 8).map(', '9.R13 cheia clientului'],
['mesajul unei erori interne in raspunsul public', MS, "code: e && /^E[A-Z]+$/.test(String(e.code)) ? 'io_error' : 'internal'", "code: 'io_error', detail: String(e && e.message)", '9.R16/R18 GET pe un cap'],
['notarizarea pe alt lant decat 28001', MS, 'if (!(chainId === LANT /*PAZA-MARTOR:sandbox*/)) return', 'if (false) return', '3. CONTROL: notarizatorul pe un RPC care serveste lantul 2800'],
// ---- linia de comanda a verificatorului
['martorii fixati nu mai ajung la verificare', CLI, 'let fixati = martori.length ? martori : null;', 'let fixati = null;', '9.R17 agent-cli verify'],
['--chain nevalid primit (paza care cade deschisa)', CLI, 'if (!/^[1-9]\\d{0,14}$/.test(String(v))) throw new Folosire(', 'if (false) throw new Folosire(', '9.R5 agent-cli verify --chain 2800x'],
['un martor care nu raspunde la --witness-url luat drept verde', CLI, "if (!c.ok) adauga({ sursa: 'list', clasa: 'NEMASURATA',", "if (!c.ok) adauga({ sursa: 'list', clasa: 'VERIFICATA',", '9.R2 CONTROL: --witness-url'],
// ---- a doua revizie adversariala B3 (2026-10-19): fiecare paznic nou, cu randul lui 10.R2-<nn> / 10.P2-<nn>
// R2-01: lista capetelor, semnata de martor pagina cu pagina, din ultimele 300 s
['forma paginii de capete nejudecata', MA, 'if (!(formaBuna /*PAZA-MARTOR:pagina-forma*/)) return', 'if (false) return', '10.P2-01 CONTROL'],
['integritatea paginii de capete nejudecata', MA, 'if (!(H32.test(sir(env.statementHash)) && sha(JSON.stringify(st)) === env.statementHash /*PAZA-MARTOR:pagina-integritate*/)) return', 'if (false) return', '10.P2-01 CONTROL'],
['semnatura martorului pe pagina de capete nejudecata', MA, 'if (!(s.ok /*PAZA-MARTOR:pagina-semnatura*/)) return', 'if (false) return', '10.R2-01 --witness <W>'],
['o pagina veche (reluata) primita drept lista de acum', MA, 'if (!(Math.abs(t - Number(acumS)) <= toleranta /*PAZA-MARTOR:pagina-vechi*/)) return', 'if (false) return', '10.R2-01 --witness <W>'],
['paginile care nu dau numarul si ultimul cap semnate primite', MA, 'if (!(aduna /*PAZA-MARTOR:pagina-numar*/)) return', 'if (false) return', '10.P2-01 CONTROL'],
['lista citita din campurile NESEMNATE de langa pagina', MA, 'witness = st.witness; heads.push(...st.heads);', 'witness = st.witness; heads.push(...(Array.isArray(r.j.heads) ? r.j.heads : []).map((h) => ({ seq: h.seq, hash: h.hash, id: h.id })));', '10.R2-01 --witness <W>'],
['lista semnata de alt martor decat cel fixat primita', CLI, 'else if (!(!fixati || fixati.includes(c.witness)) /*PAZA-MARTOR:martor-url*/)', 'else if (false)', '10.R2-15 agent-cli verify --witness <alt id>'],
// R2-02: dovezile de echivocare legate de ciobul capului contrazis; plafonul = marginea prin constructie
['dovada legata de ciobul curent, nu de al capului contrazis', MA, 'k: tinut.k /*PAZA-MARTOR:echivocare-ciob*/', 'k: cur.k', '10.R2-02 maxFrunze 4'],
['plafonul dovezilor fix (2.000), nu marginea lor prin constructie', MA, 'if (maxEchivocari == null) maxEchivocari = CIOBURI_PASTRATE * maxFrunze;', 'if (maxEchivocari == null) maxEchivocari = 2000;', '10.P2-02 CONTROL'],
// R2-03: la plafonul de chei se scoate cheia cea mai veche
['la plafonul de chei clientul NOU e refuzat (nu e scoasa cheia cea mai veche)', MS, '{ m.delete(m.keys().next().value); scoase++; }', 'return true;', '10.R2-03 limitatorul'],
// R2-04: o lista cu elemente care nu sunt capete
['un element care nu e cap primit de verificaCapeteMartorului', MA, 'if (!(rau < 0 /*PAZA-MARTOR:capete-forma*/)) return', 'if (false) return', '10.R2-04 verificaCapeteMartorului'],
// R2-05: sub un martor fixat, numai chitante ale lui
['ancora simpla primita sub un martor fixat', CLI, 'if (fixare /*PAZA-MARTOR:martor-simple*/) adauga(', 'if (false) adauga(', '10.R2-05 registrul antedatat'],
['--witness fara nicio chitanta a lui primit', CLI, 'if (!(!martori.length || dovezi.some((d) => d.chitanta) || (tinute && tinute.length)) /*PAZA-MARTOR:martor-fara-chitanta*/)', 'if (false)', '10.R2-05 registrul antedatat'],
// R2-07: un ciob nu se scoate pana nu i s-a notarizat capul de jurnal final
['rotirea scoate un ciob nenotarizat', MA, 'if (!(!plin || !rotireaPierdeNenotarizat() /*PAZA-MARTOR:rotire-notarizata*/)) {', 'if (false) {', '10.R2-07 maxFrunze 2'],
['la deschidere un ciob vechi nenotarizat sters', MA, 'if (!(faraLant || finalNotarizatPeDisc(k) /*PAZA-MARTOR:ciob-vechi-notarizat*/)) {', 'if (false) {', '10.P2-07 CONTROL'],
// R2-08: ciobul nou se naste intreg; un ciob fara ciob.json isi reface legatura
['ciobul nou nascut pe jumatate (dosarul inaintea lui ciob.json)', MA, "scrieAtomic(path.join(t, 'ciob.json'), meta); fs.renameSync(t, d) /*PAZA-MARTOR:ciob-intreg*/; fsyncDosar(dc);", 'fs.renameSync(t, d); scrieAtomic(fcb, meta);', '10.R2-08 rotirea cade'],
['legatura ciobului fara ciob.json nerefacuta', MA, 'previousShardFinal: legat /*PAZA-MARTOR:ciob-legatura*/', 'previousShardFinal: null', '10.P2-08 CONTROL'],
// R2-09: ruta capului de jurnal pentru un ciob pastrat (si inghetat)
['ruta capului de jurnal numai pe ciobul curent', MA, 'const c = shard == null ? cur : cioburi.get(Number(shard)) /*PAZA-MARTOR:ciob-cerut*/;', 'const c = cur;', '10.R2-09 un monitor'],
// R2-10: lacatul isi poarta pornirea masinii si a procesului
['lacatul de dinainte de ultima pornire a masinii tinut', MA, 'if (!(o.boot == null || aceeasiPornire(o.boot, boot) /*PAZA-MARTOR:lacat-pornire*/)) return', 'if (false) return', '10.R2-10 lacatTinut'],
['lacatul cu pid-ul luat de alt proces tinut', MA, 'if (!(acum == null || acum === String(o.start) /*PAZA-MARTOR:lacat-proces*/)) return', 'if (false) return', '10.R2-10 lacatTinut'],
// R2-11 / R2-12: codurile de iesire ale lui martor-agenti.mjs si valorile goale ale lui --witness
['receipt iese 1 la un martor care raspunde 503 sau 429', MA, 'if (r.status >= 500 || r.status === 429 /*PAZA-MARTOR:cod-indisponibil*/) {', 'if (false) {', '10.R2-11 receipt'],
['verify-receipt iese 1 pe raspunsul unui cap pending', MA, "if (obiect(x) && !esteChitanta(x) && x.status === 'pending' /*PAZA-MARTOR:cod-pending*/) {", 'if (false) {', '10.R2-11 receipt'],
['valorile goale ale lui --witness aruncate (verify-receipt nefixat)', MA, "(rest[i + 1] === undefined ? [''] : String(rest[i + 1]).split(',')) /*PAZA-MARTOR:steag-gol*/", "(rest[i + 1] === undefined ? [] : String(rest[i + 1]).split(',').filter(Boolean))", '10.R2-12 verify-receipt'],
// R2-13: bugetul jurnalului citit inaintea cotei agentului, consumat dupa ea
['cota agentului consumata inaintea bugetului jurnalului', MA, 'const bugetPlin = ', "if (limitaAgent && limitaAgent(st.agentId)) return { cod: 429, corp: { error: 'rate_limited' } }; const bugetPlin = ", '10.R2-13 cota pe agent 3'],
['bugetul jurnalului consumat la citire (serviciul fara `ar`)', MS, "{ ar: () => acceptate.ar('jurnal') /*PAZA-MARTOR:buget-citit*/ }", '{}', '10.P2-13 CONTROL prin serviciu'],
// R2-14: Retry-After prin usa gateway-ului
['usa gateway-ului arunca Retry-After', GWF, "? { 'retry-after': ra } : {})", '? {} : {})', '10.R2-14 prin usa gateway-ului'],
// ---- a treia revizie adversariala B3 (2026-10-19): fiecare paznic nou, cu randul lui 11.R3-<nn> / 11.P3-<nn>
// R3-01: un ciob de scos se redenumeste intai; un rest al formei de dinainte se sterge la deschidere
['ciobul de scos sters pe loc, fara sa fie redenumit intai', MA, 'fs.renameSync(d, t) /*PAZA-MARTOR:ciob-sters-intai*/; fsyncDosar(dc); tinta = t;', 'void t;', '11.R3-01 o oprire'],
['restul unei stergeri intrerupte tinut drept un ciob la deschidere', MA, 'if (restDeStergere(k) /*PAZA-MARTOR:ciob-rest*/) {', 'if (false) {', '11.P3-01 CONTROL'],
// revizia B3 a patra, R4-06: fiecare jumatate a restului cu plantarea si randul ei
['un rest fara ciob.json sau frunze.ndjson tinut drept un ciob', MA, "(!fs.existsSync(path.join(d, 'ciob.json')) || !fs.existsSync(path.join(d, 'frunze.ndjson'))) /*PAZA-MARTOR:ciob-fara-fisiere*/", 'false', '11.P3-01 CONTROL'],
['un ciob marcat .de-sters tinut drept un ciob', MA, "fs.existsSync(path.join(dc, String(k), '.de-sters')) /*PAZA-MARTOR:ciob-marcat*/", 'false', '12.R4-06 redenumirea'],
// R3-02: dovada care traieste cel mai mult pe sesiune
['o dovada contra unui cap din ciobul vechi tine locul celei care conteaza', MA, 'const inlocuieste = !!q0 && q0.k < tinut.k /*PAZA-MARTOR:echivocare-mai-noua*/;', 'const inlocuieste = false;', '11.R3-02 agentul X'],
// R3-03: un steag fara valoare, sau cu valoarea goala, e folosire gresita
['un steag fara valoare primit in tacere (martor-agenti.mjs)', MA, "if (!(v !== undefined && v !== '' && !/^--[a-z]/.test(v)) /*PAZA-MARTOR:steag-fara-valoare*/) throw", 'if (false) throw', '11.R3-03 agent-cli verify'],
['un steag fara valoare primit in tacere (agent-cli.mjs)', CLI, "if (!(v !== undefined && !/^--[a-z]/.test(v) && (v !== '' || GOALE_PERMISE.includes(f))) /*PAZA-MARTOR:steag-fara-valoare*/) throw", 'if (false) throw', '11.R3-03 agent-cli verify'],
// R3-04: chitanta ultimului cap tinut de martor, sub --witness-url
['chitanta unui cap tinut nu mai e ceruta (marginea o dau chitantele alese de agent)', CLI, "if (dovezi.some((d) => d.clasa === 'VERIFICATA' && d.id === h.id) /*PAZA-MARTOR:martor-chitanta-ceruta*/) continue;", 'if (true) continue;', '11.R3-04 martorul fixat'],
// revizia B3 a cincea, R5-01: numai o chitanta VERIFICATA a capului scuteste aducerea (forma veche: orice chitanta cu acelasi id, verificata sau nu)
['o chitanta NEVERIFICATA cu acelasi id (o copie veche) scuteste aducerea chitantei capului tinut', CLI, "if (dovezi.some((d) => d.clasa === 'VERIFICATA' && d.id === h.id) /*PAZA-MARTOR:martor-chitanta-ceruta*/) continue;", 'if (dovezi.some((d) => d.id === h.id)) continue;', '13.R5-01 copia VECHE'],
// R3-05: codurile lui receipt si submit
['submit "nimic nou" iese 0 pe o stare a capului care nu e a lui', MA, "if (!(r.status === 200 && obiect(r.j) && r.j.id === ultim.id && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-nimic-nou*/) {", 'if (false) {', '11.R3-05 receipt'],
['receipt pe un 200 care nu e JSON (exceptie, 1)', MA, "if (!(obiect(r.j) && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-ne-json*/) {", 'if (false) {', '11.R3-05 receipt'],
// ---- a patra revizie adversariala B3 (2026-10-19): regula de proiectare a lui --witness-url si paznicii ei, cu randurile 12.R4-<nn> / 12.P4-<nn>
['numai ULTIMUL cap tinut cu chitanta (capetele intermediare fara)', CLI, 'tinute || [] /*PAZA-MARTOR:martor-fiecare-cap*/', '(tinute || []).slice(-1)', '12.R4-03 martorul tine'],
['id-ul capului unei chitante citit fara garda (exceptie pe o forma gresita)', CLI, "const idCap = (a) => { const p = M.chitantaDin(a); const w = p && typeof p === 'object' ? p.witnessReceipt : null; const h = w && typeof w === 'object' ? w.head : null; return h && typeof h === 'object' && typeof h.statementHash === 'string' ? h.statementHash : null; } /*PAZA-MARTOR:chitanta-id-sigur*/;", 'const idCap = (a) => M.chitantaDin(a).witnessReceipt.head.statementHash;', '12.R4-02 proxy'],
// (revizia a cincea: paznicul `martor-adusa-vina` al formei de dinainte e acum clasa dovezii, pe ORICE drum: `chitanta-alt-registru`, cu cele doua sensuri)
['o chitanta care nu tine LOCAL judecata vina registrului, oricare ar fi regula (o includere stricata face un FAILED)', CLI, "const vina = l.rule === 'registru' /*PAZA-MARTOR:chitanta-alt-registru*/ || falsificata;", 'const vina = true;', '13.R5-05 chitanta agentului cu includerea stricata'],
['o chitanta a martorului fixat pentru alt registru (o ramura) luata drept NEMASURAT', CLI, "l.rule === 'registru' /*PAZA-MARTOR:chitanta-alt-registru*/", 'false', '12.P4-03b CONTROL'],
['submit pe un 200/202 care nu e starea capului trimis iese 0', MA, "(r.status === 200 || r.status === 202) && !(obiect(r.j) && H32.test(sir(r.j.id)) && r.j.session === env.statement.session && r.j.seq === env.statement.seq && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-trimis*/", 'false', '12.R4-04 submit'],
['un steag necunoscut primit in tacere (agent-cli.mjs)', CLI, 'if (!(STEAGURI[cmd].includes(s) /*PAZA-MARTOR:steag-necunoscut*/)) throw', 'if (false) throw', '12.R4-05 agent-cli'],
['un steag necunoscut primit in tacere (martor-agenti.mjs)', MA, 'if (!(STEAGURI[cmd].includes(s) /*PAZA-MARTOR:steag-necunoscut*/)) throw', 'if (false) throw', '12.R4-05 agent-cli'],
['un steag dat de doua ori primit (agent-cli.mjs)', CLI, "if (!(!vazute.has(s) || s === '--witness') /*PAZA-MARTOR:steag-dublu*/) throw", 'if (false) throw', '12.R4-05 agent-cli'],
['un steag dat de doua ori primit (martor-agenti.mjs)', MA, "if (!(!vazute.has(s) || s === '--witness') /*PAZA-MARTOR:steag-dublu*/) throw", 'if (false) throw', '12.R4-05 agent-cli'],
['departajarea a doua dovezi la deschidere rasturnata (ramane cea veche)', MA, 'vechi && vechi.k >= x.k /*PAZA-MARTOR:echivocare-departajare*/', 'vechi && vechi.k <= x.k', '12.R4-06 o inlocuire'],
// ---- a cincea revizie adversariala B3 (2026-10-19): verify ca EVALUARE COMPLETA cu un singur verdict, tipurile, asteptarea pe 429; randurile 13.R5-<nn> / 13.P5-<nn>
// R5-01: verdictul se da DUPA ce s-a adunat tot, iar o vina dovedita trece inaintea oricarui NEMASURAT
['un NEMASURAT judecat inaintea unei vine dovedite (verdictul dupa ordinea claselor)', CLI, "const verdict = vina.length ? 'FAILED' : nemasurate.length ? 'NOT MEASURED' : 'OK' /*PAZA-MARTOR:verdict-vina-intai*/;", "const verdict = nemasurate.length ? 'NOT MEASURED' : vina.length ? 'FAILED' : 'OK';", '13.R5-01 capul seq 4'],
['un NEMASURAT iesit pe drum, inaintea judecatii (forma de dinainte de a cincea revizie)', CLI, 'const adauga = (d) => { dovezi.push({ ceruta: true, ...d }); };', "const adauga = (d) => { dovezi.push({ ceruta: true, ...d }); if (d.clasa === 'NEMASURATA') { console.error('NOT MEASURED: ' + d.detail + ' (planted: an early exit)'); process.exit(2); } };", '13.R5-01 capul seq 4'],
['o copie veche a unei chitante ramane ceruta langa chitanta verificata a aceluiasi cap', CLI, "dovezi.some((x) => x !== d && x.clasa === 'VERIFICATA' && x.id === d.id) /*PAZA-MARTOR:copie-veche*/", 'false', '13.R5-01 copia VECHE'],
// R5-03 / R5-05: clasa unei chitante care nu tine: vina agentului numai pe o semnatura INVALIDA a martorului fixat (predata) sau pe alt registru
['o pana a notarului sau a RPC-ului ales de verificator judecata vina registrului', CLI, "clasa: 'NEMASURATA' /*PAZA-MARTOR:lant-nemasurat*/", "clasa: 'VINA'", '13.R5-03 registru CINSTIT'],
['o chitanta a agentului cu semnatura martorului fixat INVALIDA luata drept NEMASURATA', CLI, "const falsificata = sursa === 'agent' && numit !== null && (!fixati || fixati.includes(numit)) /*PAZA-MARTOR:chitanta-falsificata*/;", 'const falsificata = false;', '13.P5-05 CONTROL vina agentului'],
['o chitanta ADUSA cu semnatura martorului invalida judecata vina registrului (o adresa face un FAILED)', CLI, "sursa === 'agent' && numit !== null", 'numit !== null', '13.P5-05 CONTROL un proxy'],
// R5-04: regula (c), tipurile judecate inainte de folosire
['submit primeste un 200/202 al carui `status` nu e un sir ({"toString":0}): --out scris si iesire 0', MA, "r.j.seq === env.statement.seq && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-trimis*/", 'r.j.seq === env.statement.seq) /*PAZA-MARTOR:cod-trimis*/', '13.R5-04 submit'],
['un receivedAtMs negativ primit de forma chitantei (exceptia vine mai jos, in frunza)', MA, 'w.receivedAtMs >= 0 /*PAZA-MARTOR:chitanta-tipuri*/', 'true', '13.R5-04 un proxy'],
['un camp care trebuie sa fie sir trecut prin String() (exceptie pe {"toString":0})', MA, "const sir = (x) => (typeof x === 'string' ? x : '');", 'const sir = (x) => String(x);', '13.R5-04 un proxy'],
// R5-06: o citire peste limita spune cand se poate reincerca, iar verificatorul asteapta
['pe un 429 cu Retry-After verificatorul nu mai asteapta', MA, 'if (!(r.status === 429 && Number.isSafeInteger(r.retryAfter) && r.retryAfter <= buget.ramasS && i < 50) /*PAZA-MARTOR:asteapta-429*/) return r;', 'if (true) return r;', '13.R5-06 serviciul'],
['429-ul unei citiri fara Retry-After', MS, "{ 'retry-after': String(ra) } /*PAZA-MARTOR:get-retry-after*/", '{}', '13.R5-06 serviciul'],
// ---- a sasea revizie B3 (2026-10-19, verificarea tintita a reparatiilor R5): N1..N3, randurile 14.N<n>
// N1: o chitanta fara --rpc e o dovada NEMASURATA [rpc], judecata la locul ei; doua plantari pe acelasi paznic, una pe fiecare sens (iesirea timpurie
// a formei de dinainte, care ascunde ramura aratata de lista; si paznicul scos, care ia chitanta drept VERIFICATA fara lant)
['o chitanta fara --rpc iese pe drum cu 2, inaintea judecatii (forma de dinainte de a sasea revizie: folosire gresita)', CLI, 'if (!rpc /*PAZA-MARTOR:fara-rpc*/) return {', "if (!rpc) throw new Folosire('a witness receipt needs --rpc <url> (planted: the early exit of before the sixth review)'); if (false) return {", '14.N1 fara --rpc'],
['o chitanta fara --rpc judecata VERIFICATA (marginea luata fara lant)', CLI, 'if (!rpc /*PAZA-MARTOR:fara-rpc*/) return {', 'if (false) return {', '14.N1 fara --rpc'],
// N2: receipt scrie numai chitanta capului cerut, a registrului dat, cu semnatura martorului verificata (cu cel fixat, daca e dat)
['receipt scrie raspunsul adresei fara sa il verifice (forma de dinainte de a sasea revizie)', MA, 'if (!(!!l && l.ok) /*PAZA-MARTOR:receipt-verificata*/) {', 'if (false) {', '14.N2 receipt printr-un proxy'],
['receipt nu cere ca chitanta sa fie a capului cerut (--id)', MA, 'capPrimit === idc /*PAZA-MARTOR:receipt-id*/', 'true', '14.N2 receipt printr-un proxy'],
['receipt nu leaga chitanta de registrul dat (--ledger)', MA, 'ledger /*PAZA-MARTOR:receipt-registru*/,', 'ledger: null,', '14.N2 receipt fata de --ledger'],
['receipt nu cere martorul fixat prin --witness', MA, 'martori: martori.length ? martori : null /*PAZA-MARTOR:receipt-fixat*/', 'martori: null', '14.N2 receipt fata de --ledger'],
// N3: regula (c) pe drumul "nimic nou" al lui submit
['submit "nimic nou" foloseste `latest` (campul nesemnat) fara sa ii judece tipurile', MA, 'if (!(ultim === null || (obiect(ultim) && Number.isSafeInteger(ultim.seq) && ultim.seq >= 0 && HASH_INTRARE.test(sir(ultim.hash)) && H32.test(sir(ultim.id)))) /*PAZA-MARTOR:nimic-nou-latest*/) {', 'if (false) {', '14.N3 submit'],
['submit "nimic nou" primeste o stare a capului cu status care nu e sir ({"toString":0}): --out scris, apoi exceptie', MA, "r.j.id === ultim.id && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-nimic-nou*/", 'r.j.id === ultim.id) /*PAZA-MARTOR:cod-nimic-nou*/', '14.N3 submit'],
// ---- vocabularul felului (proof-kinds, citit si de judecatorul martorului) si usa gateway-ului
['vocabularul felului agent-witness-checkpoint slabit in proof-kinds', PKF, 'treeSize: [(v) => Number.isSafeInteger(v) && v >= 1,', 'treeSize: [(v) => true,', '9.R8 felul agent-witness-checkpoint'],
['usa gateway-ului primeste un POST din browser (Origin)', GWF, "if (req.method === 'POST' && req.headers.origin !== undefined) return json(res, 403,", 'if (false) return json(res, 403,', '9.R13 gateway: un POST'],
];
// PLANTARILE NEUTRE (revizia B3 a cincea, R5-05): schimba numai ORDINEA in care verify aduna si judeca dovezile. Verdictul e o functie de multimea
// dovezilor, nu de ordinea lor, deci cu oricare din ele proba trebuie sa ramana VERDE intreaga; un rand rosu aici e un ESEC al componentei (ordinea
// a ajuns sa conteze), numarat ca orice control cazut. [nume, fisier, tipar, inlocuire]
const NEUTRE = [
['ordinea adunarii rasturnata: capetele tinute (si chitantele lor aduse) inaintea lui --anchors', CLI, 'await fazaAnchors(); await fazaCapete();', 'await fazaCapete(); await fazaAnchors();'],
['intrarile din --anchors judecate de la ultima la prima', CLI, 'for (const [i, a] of toate2.entries()) {', 'for (const [i, a] of [...toate2.entries()].reverse()) {'],
['capetele tinute parcurse de la ultimul la primul', CLI, 'for (const h of tinute || [] /*PAZA-MARTOR:martor-fiecare-cap*/) {', 'for (const h of [...(tinute || [])].reverse()) {'],
];
// markerii care NU au plantare, fiecare cu motivul (azi niciunul: o exceptie noua se scrie aici, cu motivul, nu se lasa neacoperita). De la a doua
// revizie (R2-15) garda priveste si agent-cli.mjs: paznicii martorului din linia de comanda poarta markeri, deci si plantari.
const EXCEPTII = [];
const PROPRII = ['agent-policy.mjs', 'agent-ledger.mjs', 'agent-aprobare.mjs', 'agent-ancora.mjs', CLI, MA, MS, 'proba-martor-agenti.mjs'];
// surorile: din importurile relative '../<dosar>/<fisier>' ale modulului (aceeasi asezare in arborele de dezvoltare si in copia publicata)
const SURORI = [...fs.readFileSync(path.join(AICI, MA), 'utf8').matchAll(/from '\.\.\/([^'/]+)\/([^'/]+)'/g)].map((x) => [x[1], x[2]]);
const DEV_VERIFY = path.resolve(AICI, '..', '..', 'verificator-falcon', 'verify-proof.mjs');
const VERIFY = process.env.AERE_VERIFY_PROOF !== undefined ? process.env.AERE_VERIFY_PROOF : (fs.existsSync(DEV_VERIFY) ? DEV_VERIFY : '');
const GW_DIR = path.resolve(AICI, '..', '..', 'cloud-gateway'); const GW = path.join(GW_DIR, 'gateway.mjs');
const fisierPlantat = (t, d, fisier) => (fisier === GWF ? path.join(t, 'cloud-gateway', 'gateway.mjs') : path.join(d, fisier));
function copie(cuGateway = false) {
const t = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-martor-ctl-')); const d = path.join(t, path.basename(AICI)); fs.mkdirSync(d);
for (const f of PROPRII) fs.copyFileSync(path.join(AICI, f), path.join(d, f));
for (const [dir, f] of SURORI) { fs.mkdirSync(path.join(t, dir), { recursive: true }); fs.copyFileSync(path.join(AICI, '..', dir, f), path.join(t, dir, f)); }
if (cuGateway) fs.cpSync(GW_DIR, path.join(t, 'cloud-gateway'), { recursive: true, filter: (s) => !/node_modules/.test(s) });
return { t, d };
}
function ruleaza(d, gateway = fs.existsSync(GW) ? GW : null) {
return new Promise((resolve) => {
const c = spawn(process.execPath, [path.join(d, 'proba-martor-agenti.mjs')], { env: { ...process.env, AERE_VERIFY_PROOF: VERIFY ? path.resolve(VERIFY) : '', ...(gateway ? { AERE_GATEWAY: gateway } : {}) } });
let out = ''; const ceas = setTimeout(() => c.kill(), 300000);
c.stdout.on('data', (x) => { out += x; }); c.stderr.on('data', (x) => { out += x; });
c.on('close', (cod) => { clearTimeout(ceas); const linii = out.split('\n'); resolve({ cod, rulat: /martor-agenti: \d+\/\d+ cum trebuia/.test(out), rosii: linii.filter((l) => l.startsWith(' RAU ')), sarite: linii.filter((l) => l.startsWith(' -- ')) }); });
});
}
// o plantare NEUTRA: proba rulata si verde intreaga (zero rosii, zero sarite in afara usii gateway-ului)
async function neutru([nume, fisier, din, inl]) {
const { t, d } = copie();
try {
const f = fisierPlantat(t, d, fisier); const src = fs.readFileSync(f, 'utf8');
if (src.split(din).length !== 2) return [false, ` STRICAT (neutra) ${nume}: tiparul apare de ${src.split(din).length - 1} ori in ${fisier}`];
fs.writeFileSync(f, src.replace(din, () => inl));
const r = await ruleaza(d);
if (!r.rulat) return [false, ` STRICAT (neutra) ${nume}: proba nu a ajuns la rezumat (cod ${r.cod})`];
const sarite = r.sarite.filter((l) => !/usa gateway-ului/.test(l));
if (!r.rosii.length && !sarite.length) return [true, ` VERDE cum trebuia (neutra) ${nume} (proba intreaga verde: ordinea nu schimba nimic)`];
return [false, ` CONTROL CAZUT (neutra) ${nume}: ${r.rosii.length} rosii, ${sarite.length} sarite: ${[...r.rosii, ...sarite].join(' | ').slice(0, 300)}`];
} finally { fs.rmSync(t, { recursive: true, force: true }); }
}
async function planteaza([nume, fisier, din, inl, tinta]) {
const cuGw = fisier === GWF; if (cuGw && !fs.existsSync(GW)) return [true, ` (sarit pe nume: ${nume}: gateway.mjs nu e in aceasta copie)`];
const { t, d } = copie(cuGw);
try {
const f = fisierPlantat(t, d, fisier); const src = fs.readFileSync(f, 'utf8');
if (src.split(din).length !== 2) return [false, ` STRICAT ${nume}: tiparul apare de ${src.split(din).length - 1} ori in ${fisier}`];
fs.writeFileSync(f, src.replace(din, () => inl));
const r = await ruleaza(d, cuGw ? f : undefined);
if (!r.rulat) return [false, ` STRICAT ${nume}: proba nu a ajuns la rezumat (cod ${r.cod})`];
if (r.rosii.some((l) => l.startsWith(' RAU ' + tinta))) return [true, ` ROSU cum trebuia ${nume} (randul '${tinta}' pica)`];
return [false, ` CONTROL CAZUT ${nume}: randul '${tinta}' a ramas verde (${r.rosii.length} rosii altundeva)`];
} finally { fs.rmSync(t, { recursive: true, force: true }); }
}
// ACOPERIREA: fiecare aparitie a unui marker PAZA-MARTOR din MA si MS sta in tiparul unei plantari (sau pe EXCEPTII); intoarce markerii neacoperiti
function acoperire(plantari) {
const lipsa = []; let total = 0;
for (const fisier of [MA, MS, CLI]) {
const src = fs.readFileSync(path.join(AICI, fisier), 'utf8');
const spatii = plantari.filter((p) => p[1] === fisier).map((p) => { const i = src.indexOf(p[2]); return i < 0 ? null : [i, i + p[2].length]; }).filter(Boolean);
for (const mk of src.matchAll(/PAZA-MARTOR:([a-z-]+)/g)) {
total++;
const rand = src.slice(0, mk.index).split('\n').length;
if (spatii.some(([a, b]) => mk.index >= a && mk.index < b)) continue;
if (EXCEPTII.some(([f, nume, ancora]) => f === fisier && nume === mk[1] && src.slice(Math.max(0, mk.index - 200), mk.index + 50).includes(ancora))) continue;
lipsa.push(`${fisier}:${rand} ${mk[1]}`);
}
}
return { total, lipsa };
}
let rele = 0;
for (const [dir, f] of SURORI) if (!fs.existsSync(path.join(AICI, '..', dir, f))) { console.log(` STRICAT sora ${dir}/${f} nu e langa componenta: copia nu se poate face`); process.exitCode = 1; process.exit(); }
const ac = acoperire(PLANTARI);
if (!ac.lipsa.length) console.log(` OK acoperirea: toti cei ${ac.total} markeri PAZA-MARTOR din ${MA}, ${MS} si ${CLI} au plantarea lor (exceptii: ${EXCEPTII.length})`);
else { rele++; console.log(` ESEC acoperirea: ${ac.lipsa.length} din ${ac.total} markeri fara plantare si fara exceptie scrisa: ${ac.lipsa.join(', ')}`); }
// martorul acoperirii: cu o plantare scoasa, garda trebuie sa numeasca exact markerul ei (altfel o garda care nu vede nimic ar iesi verde)
const scoasa = PLANTARI.findIndex((p) => p[0] === 'promisiunea nejudecata');
const ac2 = acoperire(PLANTARI.filter((_, i) => i !== scoasa));
if (scoasa >= 0 && ac2.lipsa.length === ac.lipsa.length + 1 && ac2.lipsa.some((x) => / promisiune$/.test(x))) console.log(` OK martorul acoperirii: fara plantarea '${PLANTARI[scoasa][0]}' garda numeste markerul ei (${ac2.lipsa.find((x) => / promisiune$/.test(x))})`);
else { rele++; console.log(` STRICAT martorul acoperirii: fara o plantare, garda nu a numit markerul ei (${JSON.stringify(ac2.lipsa).slice(0, 200)})`); }
// --numai-acoperirea: acoperirea si unicitatea fiecarui tipar in fisierul lui, fara rularile probei (o verificare rapida in timpul lucrului)
if (process.argv.includes('--numai-acoperirea')) {
const { t, d } = copie(fs.existsSync(GW));
for (const [nume, fisier, din] of [...PLANTARI, ...NEUTRE]) { if (fisier === GWF && !fs.existsSync(GW)) continue; const n = fs.readFileSync(fisierPlantat(t, d, fisier), 'utf8').split(din).length - 1; if (n !== 1) { rele++; console.log(` STRICAT ${nume}: tiparul apare de ${n} ori in ${fisier}`); } }
fs.rmSync(t, { recursive: true, force: true });
console.log(rele ? `ACOPERIREA: ${rele} probleme` : `ACOPERIREA: ${ac.total} markeri acoperiti, ${PLANTARI.length} tipare unice, ${NEUTRE.length} neutre (probele NU au rulat)`); process.exitCode = rele ? 1 : 0; process.exit();
}
const c0 = copie(); const m = await ruleaza(c0.d); fs.rmSync(c0.t, { recursive: true, force: true });
// martorul: proba ajunge la rezumat, zero rosii; un rand sarit e primit numai pe nume (usa gateway-ului cand gateway-ul nu e in aceasta copie)
const saritePermise = m.sarite.filter((l) => !/usa gateway-ului/.test(l));
if (m.rulat && !m.rosii.length && !saritePermise.length) console.log(` OK martorul: copia neatinsa verde (cod ${m.cod}${m.sarite.length ? ', ' + m.sarite.length + ' rand sarit pe nume: usa gateway-ului' : ''})`);
else { rele++; console.log(` STRICAT martorul nu e verde (cod ${m.cod}, ${m.rosii.length} rosii, ${saritePermise.length} sarite: ${[...m.rosii, ...saritePermise].join(' | ').slice(0, 400)})`); }
const LUCRARI = [...PLANTARI.map((p) => () => planteaza(p)), ...NEUTRE.map((p) => () => neutru(p))];
const rez = new Array(LUCRARI.length); let i = 0;
// cate plantari ruleaza deodata: AERE_CONTROL_PARALEL (implicit 12). Masurat 2026-10-19 pe laptop (16 fire): dupa prima revizie 4 deodata 529 s,
// 8 deodata 311 s, 12 deodata 270 s; dupa a patra (112 plantari) ~475 s cu 12 si ~747 s cu 4; dupa a sasea (134 de lucrari:
// 131 de plantari si 3 neutre, proba de 158 de randuri) masurarea e in README, randul acestui control (pana la a sasea revizie comentariul trimitea la
// masurarile de dupa a cincea, 126 de lucrari, care nu ajunsesera in README). Rulatorul B2 (scripts/deschise/ruleaza-probe.sh) da acestui control
// 1650 s din a sasea revizie (1200 s pana atunci; aritmetica in probele-b2.sh); fiecare rulare a probei din el are 300 s (ceasul din ruleaza()).
const PARALEL = /^[1-9]\d?$/.test(String(process.env.AERE_CONTROL_PARALEL || '')) ? Number(process.env.AERE_CONTROL_PARALEL) : 12;
await Promise.all(Array.from({ length: PARALEL }, async () => { while (i < LUCRARI.length) { const k = i++; rez[k] = await LUCRARI[k](); } }));
for (const [bun, linie] of rez) { console.log(linie); if (!bun) rele++; }
console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `DOVEDIT: martorul verde, acoperirea intreaga (${ac.total} markeri), ${PLANTARI.length} din ${PLANTARI.length} paznici scosi -> randul lor rosu, ${NEUTRE.length} din ${NEUTRE.length} plantari neutre (ordinea) -> proba verde intreaga`);
process.exitCode = rele ? 1 : 0;

View File

@ -0,0 +1,385 @@
{
"v": 1,
"kind": "aere-agent-witness-evidence",
"network": "eip155:28001",
"rpc": "https://testnet-rpc.aere.network",
"witness": "aere-witness:fe5657cab99b3636a22cdc68b2dc952e77e9223d",
"policy": {
"policy": {
"v": 1,
"kind": "aere-agent-policy",
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"spend": {
"amount": "1000",
"windowSeconds": 3600,
"asset": "AERE"
},
"tools": null,
"recipients": [
"0xbbbb"
]
},
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b"
},
"ledgerBefore": {
"version": "aere-agent-ledger/2 (2026-09-29)",
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n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\nk/orF/12\n-----END PUBLIC KEY-----\n",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"entries": [
{
"seq": 0,
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792387118,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792387118
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 0
},
"signature": "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",
"hash": "932ce0bd9da3549bfb83473da0ef076104efa0e6a592fe452a1adfb0a0570f7e"
},
{
"seq": 1,
"prev": "932ce0bd9da3549bfb83473da0ef076104efa0e6a592fe452a1adfb0a0570f7e",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792387123,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792387123
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 1
},
"signature": "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",
"hash": "6032a854c357ad3f1aeacb7419bf3c3f0c29bc34b84024bd18a12c54c7300f99"
}
]
},
"head": {
"v": 1,
"kind": "aere-agent-ledger-head-attestation",
"statement": {
"v": 1,
"kind": "aere-agent-ledger-head",
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"session": "86f9d221fce6611824990720800c7d36",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"seq": 1,
"hash": "6032a854c357ad3f1aeacb7419bf3c3f0c29bc34b84024bd18a12c54c7300f99",
"createdAt": "2026-10-19T05:33:38.130Z",
"previous": null
},
"statementHash": "0x25f1993926057b1da46cd815b3e7ae0d84d4ac777344c4476a87f4e3c13966fc",
"signature": {
"scheme": "ml-dsa-65",
"publicKey": "-----BEGIN PUBLIC KEY-----\n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\nk/orF/12\n-----END PUBLIC KEY-----\n",
"signature": "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"
}
},
"receipt": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint",
"root": "0x1b4af9bfc354446fc9ea2ec1583e02fe916f9aa83862c64230f699806fde2027",
"log": "aere-agent-witness/28001",
"witness": "aere-witness:fe5657cab99b3636a22cdc68b2dc952e77e9223d",
"chainId": 28001,
"treeSize": 1,
"maxMergeDelay": 1800,
"createdAt": "2026-10-19T05:33:38.138Z"
},
"statementHash": "0xe1fcad7a17d13430cb3295fb26efe1aa8e2695fa7f1c35dfcfed56d5d17994bd",
"signature": {
"scheme": "ml-dsa-65",
"publicKey": "-----BEGIN PUBLIC KEY-----\n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\nUM6NT1Wq\n-----END PUBLIC KEY-----\n",
"signature": "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"
},
"notarization": {
"chainId": 28001,
"notary": "0x70099E62735500AA2F85B60C518551a57B202d54",
"firstSeen": 1792388029,
"txHash": "0x41fcb96d337eef07e2ce99739ac2a0940e501c76cee9e18fe252625842f69227",
"block": 6828096,
"submitter": "0xBAD08a6e956b77cAD3f66823BA7a6DD47005d131"
},
"witnessReceipt": {
"format": "aere-agent-witness-receipt-1",
"head": {
"v": 1,
"kind": "aere-agent-ledger-head-attestation",
"statement": {
"v": 1,
"kind": "aere-agent-ledger-head",
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"session": "86f9d221fce6611824990720800c7d36",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"seq": 1,
"hash": "6032a854c357ad3f1aeacb7419bf3c3f0c29bc34b84024bd18a12c54c7300f99",
"createdAt": "2026-10-19T05:33:38.130Z",
"previous": null
},
"statementHash": "0x25f1993926057b1da46cd815b3e7ae0d84d4ac777344c4476a87f4e3c13966fc",
"signature": {
"scheme": "ml-dsa-65",
"publicKey": "-----BEGIN PUBLIC KEY-----\n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\nk/orF/12\n-----END PUBLIC KEY-----\n",
"signature": "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"
}
},
"receivedAtMs": 1792388018133,
"receivedAt": "2026-10-19T05:33:38.133Z",
"leafIndex": 0,
"treeSize": 1,
"leafHash": "0x1b4af9bfc354446fc9ea2ec1583e02fe916f9aa83862c64230f699806fde2027",
"path": [],
"promise": {
"v": 1,
"kind": "aere-proof-of-time-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-time",
"subjectHash": "0x25f1993926057b1da46cd815b3e7ae0d84d4ac777344c4476a87f4e3c13966fc",
"source": "aere-witness:fe5657cab99b3636a22cdc68b2dc952e77e9223d",
"at": "2026-10-19T05:33:38.133Z",
"createdAt": "2026-10-19T05:33:38.133Z"
},
"statementHash": "0x0a3c2beb66b6e2cf31bbfaf80fae2ff6888e369c483cdce7c0aba32f2ced7658",
"signature": {
"scheme": "ml-dsa-65",
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIHsjALBglghkgBZQMEAxIDggehADYE41hQ1spV//pznI7Wow4hMxrg3p6hb+3P\nPsdJBWLj0RNT1AT7Wh8SnuC6DeYtw45G/0xzXsFXVpmiG1QGXx0ey6fkxiJACVpl\nPLbLBAbM0IHDyhk+g94cuuV3FPHDgnA4sK4Fvb/KtKMILCfv161hAbGSPYDdIMXx\nYxIHkCkBb9Z0ob2iUabmPKjWWpTUKAIK2Ed9lsqCgedEFaH//XDPMdke7zqeTDky\nJvSDJARMFX++jgCle1Heo4DSYwDsE8/XYL37F+0w+1EZUuIlQJf2nfHLPRrpz8nA\nEB/LPRxImNNoiTvVH11GQ2HqQ1ufDcRSHUAEe8ZKU8TXafWWIQUyV4ITcod4xi1I\nMgvJXWdZRTUQg5HuzUcC/HfSLTdN2/acrmdMql80NAPNJRPykVAdo0Jc3TDwUMsV\n/YBVs2UM2/SsDjzCcE9h/1S5/nUdKm0ym6KKfu5LHSFet0Xlmd0mvTA96hTXlie6\nu+pElxTpyDwAr2mbcfp1fsaMKtJGJXQ9IEXjSHnZIr4PECsNVHgTUKUGKl/5Umby\nEQKg8Dm6RBnb2snVw4f7PtTpBuouqoSkA1Ihd5Y5/ODLZs6Pp1tngCb6LUFBKi6C\nJl4qpYcdHXzh+/nZ/NUTAcJ0tTE7b3a4rtaRcNBB9HhTzAyk1kRiQn0lE4cQdwA2\nnicjItLJEQ17CnLsHt+b8PzrWglVqZkGKbya8yIfWRv5I62d/HBW58cdSlLcKAuz\nQ/crlLLojWAvp66G2nVQiiP5NHbru9rS/ui6xtIpgyo4Kk96AYWEeX1r7olu/ZHA\nTN3BYdtOV+P7DEgJC/wiMUQb5gHKAclqUR4zD3VTBYwO6bPq43UVuPoDN/Hzv1Lu\n+8rlraKzEJt1SXt7HoNA52afe6jI4gFoDpZFnL9Z+S+aRUWNJDYuTnnYAVcUfxqV\n9bRoMoO+/puKcOFt7TymM9Kes3B7jFT3ae8khUu/a22kSVISU1moXH5Snm2lqtzl\n3Q+bLTzbcuELonilCyJtsMTYZRCeYKtmZUpfohFDZdBFZV46tNRNBPgkO5P5BsBr\nk53DvNy/ZHJkrKmNY6jv0foXskFoJa0DW45NbmzgxZtOrPCFJSi9keTC93PAkjK2\nOl4m7n0v6CV+D1jJ+Jijhj/q7oKSoRUoo6Voj3ag0YBBlrs+HoqE28PH4oyppVFq\nr5Tda3HYnMO7AHLuusObi6CRKf0MlKZk//BKVywwl3Fxg1VH2PQvgibQg931IlyW\nSJXxu1vgeF0NtHyU9kjSMkc5gpf5rSjRcCPJOGEMHc9SVpZPSBBjHg4TP7z/jeMt\na/wE9r1MzwyZsf2NyjSjkQkKJJqNXod217aLgqxHR6TQO6awGbCBV1GqFz3lYHx2\nd6Ss6Dgf51Gqt8fKg8KtvUmiZzJItqO3yLkcUi+9IbVmygOYNQJjG+/b1RTzWUPh\nPlPonMuS01lcONGDzaQhHGwKz+gqCqvBbeHD9UL039Ul8CaYYcDXHgrym3E2qckl\n1RyDF6Bl892hK/tY69LF5CL1ITD2UGt8s9KiUaAeiOCJWVzXNB+UmHgWn4hVPazv\ne7iQY8qklcppSQ95XYIjnL2LcTDIYxZ3N4JyrFwcHzE4m/VuT1fvluuu3SXRPCPw\nhvirkibuay4tDq5AzSeIWfG+QWrVA7T8d69EscnNecYFEl7ZtKqnOgRzgPpUQXiC\ndHS3WVChkiX+8eh1C8FirPU5zPxgCyKx3m4JIQ8AAVlKE3GqVdAjZ9Mmy8grvOvS\nwim/HX674yNMJ0Ctpx0mS9N0VwPIRGIfPdmA5bxrxy9XgJe8OKKkwhPvg16ZLpek\nS7TfTCaKznwVEEvHB+nFTOTB/AxIDIixRCX6koAKqXpYhpAPh7rNdZsPQTZNfuDW\nuw7To0E2pGAUz6C81YEMb2Pe1i58uu6S51JX66JT/oVy8KxR2NICXshxkvkEacnq\npn9OoEhPw4D0zFVl8jblmBHrLYlGDpz6cvi4ZvVwDq//2VfGTRQGHD1lH9sIB8YJ\nFZG4Tdy9GDPFKbZJ42Zm4RZkfHKm3pcLAUzwu9ncDPqLes6FcAv2xlg09rXIwDel\nTgxwshR2KtCvSPRD8mgmS8+/j9d9wUARGA82iZ0ylcF7Ssov7ABUr51UE2H9BeEt\n/j+YyLo1ylyLl/j1IePXJsLjw6sIlZeNZPRa1ul+wYc/IMqWgMv3voIJYM0qWxaU\nMZgq5G+gRy5lHxSU/5+a067qH1LGa52Bv+kFiJd4ygidDkTYy3jATZG2Y+O53nIk\ne21jr3pGtX1JiB46U5BT9QM+rKe1Ie+OtowDj7zMZEOj5Nwy4v0h7UJegZXoviQu\nlvLhjY+NcbGqqKBTw/KVUKRY6/GVxe2kwtSnEoxgWQHsABsyDSq3W0f2UQ80m/Th\nnP/ypKXMpWN0htdoB8GOCVsJcGb0iM5iY5vKhCOSwc2G/xLc6Yybl/CjeRspkvvy\nDxLPF65chLckonXtxUI/X2WuBSZodOd/eGWH+OhHStl9ObCqWxsiyBUyEo8l/05u\n4PdqgCEbYuliHWvz1aXNRNjjcat8mgaZdnAS/PFR5chEmDyTiYns0qpgKJIVT3lq\nUM6NT1Wq\n-----END PUBLIC KEY-----\n",
"signature": "JcEg29GfYDcK6472yQU8XArpv2yDGvApEij4OZJDZMGgZtsgKlXA9xdjxigb/W0KdJhCeHu1f6R1ZJtmbry/tK2g5o33RQgYiU8DHHqLqGsJFJfRzDA4tE4ws/EI4flRoYaiwGZzgztqywP5DTMSyZs9Yb3jH35y2hcmI4Mc2IAQCQBAxvmQ3T0hp6Ny8aJmsjgpU8qidPYuBTEseUeQl9gKFKUMjXRPRe6lna6N1NaTqYlb/Gumdir+6/IdnIMnMwCXqGyIxhCRJECaKmkBHjydkmzAk+Ne+DIjJQxk89PFK7AWg729u43rSSavtU4Ajrotgear0mcw9Onje8rQpZb1noNbdRxlqse/lEo2oLB74wJ5SZ9S2l1y44aec1yMvZb3zPFp5CPHqO2hRfBPDpLx3olSvp6n1jvaiG+jewpVOs2VQcD/4ypkNgNIpAzg35ngpxSkB6Brk8urQnEg8apMcn+AS+WISf57xIPw89+px+qSgBx+pALjlPUO4BjgVyTxGGjNvo/hwrN7lyyt33Y9uek924xhT4hqrFVX29iXbafkczlSUwn3BKzew6amszGk254tiOmT2zIW550+o/TJ09d4ZK9IuZYsnB+Wi/CGrrdigI7CyhvJJFVzjyKLFH61Pu22p74LYHMlP4cY4DnpwW8r7+8r0XczSBqEiZ/uLbkzfq0WsSdIaQZHNvg14bv/Z/dthaHX9br7gVIacCaguGo9TyO7M3aiEn2GzawIYWd9MJzY7j60J5KplhMV8UuA5MCZ/LXjbplAukS3zOA9pEjexZTixUh7ACu+2Jej0rHhQ4ybOt/fPcnODuuAdNZrsd63D/mooojVp7Bscth1p/sxYBe466OA9JCTRNuHHuOjRY22Q1d3X4n7XNRFaIXpYr0rY6Z1cNsBEAk2IBmSBiEyIMyH66AhSOdMi+G8kzpJGVdmLq0eUIWZKK20qtMugCDpKZPdVMSlc3FC9Wn+FGTME5zKHbGrRkdfhPDv34zZtSXwltXLLtfLb5QMDg1LT6yxRF4MUrTv1ZBteeJM11iek8y8MBcnL43FHQpzOjc3vNcveshtpOHnIU3Po+JOVrWjLdX/y6gLZ4l4b3fpdnNokEVV4tFkQdwS8ivdkRtpO2lif+dA0A84D2+wyA1iflx39agdXYPSh1r3jtn317eTXjBKSIctPgk8gcu/BePqNwzJpGy2xMOa+yZYGwAQi18W6uCwWJ4XeL5sTqQvsodIqzw2kLuHUy4pj2cjc/7bI/haRZmck64V1Zg6YmA0S15k1JmMBa/iOrpfnRDvkwz5j9g2Ux/2TcqI9aTFQe+Y+h+N7I+gzkw+rfPFA56js44Y0UDAEsLFo5GA+BsdybOaZi3pZaR36HdmE8nvuPG8yQe/ZEKHgnpb6ZFETzfUWrztYbvF3CagVgfFccRfDAqLw7IRzqrJvHyc+6LfV8S4P70CkgE4eNOn5MNVRVuHlMgI8ruTj3JasVQCdykhZ2qPwnwZdSZ4eTJK09uDal02FmWB0wmyM9iBRhXI6Djeq6zGm0UTTJHtSkL3JU7N//2wKdI/qACrZshf6VHTdOJFSU8RNVVDhnxC04hI375ntW5dxHFDnITo9YXS1ieffcpD9kS2TxLD3dZLhTxDf7aNLhozBOWAr83rsnCDh+85AqqpshSII1tRw420w2ANUnrlesucd95QKMUZanlujBncnfbp+2acP/jlvYGSw8qTUSP3RQGPJL+l82ETUlTKEVS7vKVLCm0w14phNMbuM4RB5qfIu/HxEedciTUaWI1N20gj7xWVml2xR9gCFdt7pYCmmhA3mIXXRlMwvf5G2o7E0BiVwf2Xwnzaeu1UbXhw6gGHvrC6uo04SQpIU3n3RNCbccY6rIQNLijlKhqavCAkX+2kJwLLZvTn9RTQDyS/cIyMmRiBvAr7zpNgeYkDBVAKjRCV7m0okq7TWXImKCybsdW8Bgf/qbcT5vleb4UtUHuUh67Da0up+V0FgRsRuYaklxUBFJjtFL7yGdVK6InX0WKeXN7LEIF5+84ELTG12QWXGkEUzB9V2IPj/beIVj8gILFEtZcxv38DC51QYRaXIV6nW9s07r+EzC0tLDjhHDePJio4pElFAnQAJjp9Q9N+lU1ir6dTeqVsHhNGEpQTQvU7kXmBIHsqin0wVnp64Sek5Pt34WEKXK2nzEUpmZT/u7XLwUvmIll4WLLVa5E84B04bDzjr7Wp8KM+6uWdKvQZp+wSRi5W+PjMzuSKgGkRP95vZri6IejWbxZWKr2GZifF9n10s6ZTYW1OXQGBD3pAUzahebt3PxuyId0UPnv9rl8n2Q4J5Lo5kvJwVVCelI2NJ1Pj4XhSpCAL43NzJQi3KQ1MnE6E36cQN4g4C+zhgVcM4SxRbkjR7bhhtdsp7PsKbgGkfvcsO4FfyeRxb4vi6H+hO5VBRK6EHiBlpVR2iMa+LNIISeR8tnX3FtnyYoEe8dVeeXz2YFTJSDcPMO/OgtTUFsQ3NcfJjbHVoRVnvd2u5NK+jAonhuUqhHsRkfRtKLDpzxwiXaJkfhMghptxXs9HyxfYIGY3x5iubbtKsfLjdwJi8Z5Sp3gSS3SOGUkx2Ztt/5Li1/1z8S76g08LpHFgqcyULBdAWnt+CniaEzDK5I/kI6pNE3Aalo1/aLKRRFSW9B83u/kfJoxBZ2+fj5fpsPbfJ7tUs7GHtWexlPlfyor69d9ekGprEEsliBh7v+jRWqK1Pkr0AuvJzHmil40KRwO8tWIm7LSQtoXDyv/xkPG87NlM7v+sQJLcQiPCLVh+90GXTjJdi2b+ExCLKLtwWu2h5vf6eXJW6my+ERcOtqK6wHjIX/9V1U3dU6sQnKqxaHGLfQ+XjsRA71hk6GA3WEQ7TWOJvrqNqIZCcPr8Ge85acUnkmr+trdd86+mlhZraPoRHVzBcBjm4qsj/foY1uKu41T4BrOgGuR5bje5k7hZOfwzRXNyntKH7j9LXWvNZh3erugfXD3ImcJmXM4X3kgFQdZa1z7inzF8NoK+q5B3tgoy/V5HycWHouSu3AaIUcR7/QDJEeiBXiNvHlw8eKA74BaYgLSRW9SClAkIKloYcDEZHgHlVt7imerOnrMAX9XHdUW7mR9nCJ6uEuV/b9oweFzL2hZwLAHlXMrHnhMzeNFKdnPr+ByD2+fo3o2vTstyjCp2rHb0SIsrmd8qrGcmpThhhjBDiDXPCpnulzkpyNDyZcEHwzTkAxIJ24GyJlNdQAB50J8RV/cnqn3EPAJp0uPpzSblJqU4uSy1cB7GpuOrzUrWhMb9rOjXa8SjF1hrlJQagY9d7LBdX6Nglum1hojBAhxYMLgK0eoAUvA76uHw2F4rW7ZeK62S6OJOwxJdKzQKXvCZ9Hm7/6twmEhsrl01kSGFB3iW6wm9YbzBdUPiuB8FEVpi+WSOkZcSpOo6JIuXTF1/B/MZzGp6arwXpFxpP/SpLR8vW+v5L5rsh/KHMCJWEMNAPdnEUBpW7Y5l6Ngjtur4gAeWye5xYx9HUPBDjLP2meRMCYAR+df/5SV1azCcFKHQu9jR7ThfeNf+5cNOb/oTNW/kQt1N/BUcpisJy04Df5hv7ry8vgO2arMK36sCeOATQPfMHGvxMvfUyR6v0Fi4/wOlW7OvHEH2MRYfnYMpW8g4e8xkduIPH9sUqkYuiSqn8xiSLs4kgKoUgP65Jx9cbC7T6E8owl2lBudwVgDCxs6eu/ux8sG2ZIPT/OAQvF+gHYIaVuPdyroJnXJpzbk3DE4I+YauQJJYu/tLtVmbKo4PUQ5DYO7469Zh6DkqaELV2OfcysmjSUP4wJz31cgE+SFrqDswNGen1nJQmxREhH5ziiupFYHognHBVBfB/evybJ/X5loH45bmJ6KBEFeYZpfcwOJK/xfBfR3WfATYexH5fCbfaJP8EAXbnSnf1n0XKtNbaJ7NGEnJy+I3qYO/8EAl7NRiR+CHV8aVk0sVa/ZENvtlwx8gJe0taCDA6cMe4/lYtl0UWBEG7QrNN8Dhe98JbJSYS+tVmICrAebnBdc9xWReF/FI/KRRGwMvl3/FS/bKz6DL2Dxq9hQ1UJii67vnpMZI71fmKniZqcQwXNGmXtybQsSOVXOoQcBPo9OPEw9f2qpxaFAAgEYRAUAx+XTKnLkAEwN4jzw375/gXZrq53AyEwa4RQlxY9/PxcdI2v49aEzh9cbfPqb8eyB6Unf5nYkpdv+FEHkn7EEbQPpBsHShDIViEfGNgLRIxrOZGB1KIHPpbk8kLi2DHpQQFuRQBr3FyuJmB0YE6FVgOZvrBenfsYmTLG6RpC01O3bQ/wtXYGaVwGCFnsXj+wB7srPAV1llxAcXGDM3dXmPpAAAAAAAAAAAAAAAAAAAAAAAAAAABQsRFhoj"
}
}
}
},
"aip23": {
"verdict": "VALID",
"levels": [
{
"level": "form",
"state": "PASSED",
"detail": "kind=aere-proof-of-agent-witness-checkpoint-attestation"
},
{
"level": "integrity",
"state": "PASSED",
"detail": "sha256(statement) == statementHash (0xe1fcad7a17d13430..)"
},
{
"level": "signature",
"state": "PASSED",
"detail": "ml-dsa-65: verifies over the canonical statement text"
},
{
"level": "finality",
"state": "PASSED",
"detail": "post-quantum: the state of block 6828127, the parent of anchor 6828128 whose post-quantum certificate verified here records this statementHash in AereNotary 0x70099e62735500aa2f85b60c518551a57b202d54 as first seen at 2026-10-19T05:33:49.000Z (1792388029); first seen in block 6828096, transaction 0x41fcb96d337eef07.. (located through the endpoint's logs, not part of what is certified)",
"certified": {
"chainId": 28001,
"notary": "0x70099e62735500aa2f85b60c518551a57b202d54",
"firstSeen": 1792388029,
"stateOfBlock": 6828127,
"anchor": 6828128
}
}
]
},
"honest": {
"version": "aere-agent-ledger/2 (2026-09-29)",
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n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\nk/orF/12\n-----END PUBLIC KEY-----\n",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"entries": [
{
"seq": 0,
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792387118,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792387118
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 0
},
"signature": "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",
"hash": "932ce0bd9da3549bfb83473da0ef076104efa0e6a592fe452a1adfb0a0570f7e"
},
{
"seq": 1,
"prev": "932ce0bd9da3549bfb83473da0ef076104efa0e6a592fe452a1adfb0a0570f7e",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792387123,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792387123
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 1
},
"signature": "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",
"hash": "6032a854c357ad3f1aeacb7419bf3c3f0c29bc34b84024bd18a12c54c7300f99"
},
{
"seq": 2,
"prev": "6032a854c357ad3f1aeacb7419bf3c3f0c29bc34b84024bd18a12c54c7300f99",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792388083,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792388083
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 2
},
"signature": "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",
"hash": "e6dab29070b8a57506ead833ae1b6951148cb6da350cf6ed2b675c2352ba2059"
}
]
},
"backdated": {
"version": "aere-agent-ledger/2 (2026-09-29)",
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n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\nk/orF/12\n-----END PUBLIC KEY-----\n",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"entries": [
{
"seq": 0,
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792387118,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792387118
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 0
},
"signature": "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",
"hash": "932ce0bd9da3549bfb83473da0ef076104efa0e6a592fe452a1adfb0a0570f7e"
},
{
"seq": 1,
"prev": "932ce0bd9da3549bfb83473da0ef076104efa0e6a592fe452a1adfb0a0570f7e",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792387123,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792387123
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 1
},
"signature": "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",
"hash": "6032a854c357ad3f1aeacb7419bf3c3f0c29bc34b84024bd18a12c54c7300f99"
},
{
"seq": 2,
"prev": "6032a854c357ad3f1aeacb7419bf3c3f0c29bc34b84024bd18a12c54c7300f99",
"body": {
"v": 2,
"agentId": "aere-agent:fc3651d19506cc9b91d84ac9e4cae2f6673af0df",
"policyHash": "0x835511d0a8a352fe7cc116833d444ea8497326a5332be0553475aa40289afe3b",
"session": "86f9d221fce6611824990720800c7d36",
"at": 1792387418,
"action": {
"kind": "payment",
"to": "0xbbbb",
"amount": "10",
"at": 1792387418
},
"decision": {
"allowed": true,
"reason": "under the limit"
},
"provenance": null,
"seq": 2
},
"signature": "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",
"hash": "8720911f2ada1371c8795d5275583ebcef46408260c8943445e1b5e275bd5db4"
}
]
},
"verdicts": {
"honestWithReceipt": 0,
"backdatedWithReceipt": 1
}
}

View File

@ -0,0 +1,245 @@
#!/usr/bin/env node
// martor-agenti-serviciu.mjs: SERVICIUL martorului gazduit al agentilor (B3, 2026-10-19), sandbox catre testnetul 28001. Un proces pe loopback
// (Node 24: ML-DSA-65 din node:crypto), in spatele gateway-ului Aere Cloud (/v1/agents/witness, cum serveste adunatorul scutului /v1/shield/*):
// POST /v1/agents/witness un cap de registru semnat de agent (martor-agenti.mjs cerereMartor); 202 primit, cu
// promisiunea semnata de martor; 200 deja vazut; 409 echivocare (dovada intoarsa, si
// pastrata: una pe agent si politica), cap vechi, sau cap care nu continua ultimul; 4xx
// refuz numit; 429 ritm; 503 bugetul jurnalului pe ora (log_busy, cu Retry-After)
// GET /v1/agents/witness/0x<id> starea capului: pending | checkpointed | notarized, cu chitanta (plicul AIP-23)
// GET /v1/agents/witness/aere-agent:<40 hex>/heads?session=<32 hex>[&from=<seq>][&limit<=100] o pagina a capetelor unei sesiuni, cu
// pagina SEMNATA de martor (`signedPage`, cu momentul ei), singura pe care o crede verificatorul
// GET /v1/agents/witness/aere-agent:<40 hex>/heads[?after=<session>] sesiunile agentului (cel mult 100)
// GET /v1/agents/witness/equivocation/0x<id> o dovada de echivocare pastrata
// GET /v1/agents/witness/checkpoint[?shard=<k>][&from=<size>] ultimul cap de jurnal al ciobului curent (sau al ciobului pastrat `k`, si al
// unuia inghetat: cel final) si cel notarizat, cu dovada de consistenta in acel ciob de la `from`
// GET /v1/agents/witness/health starea martorului si a limitatoarelor (chei tinute, scoase la plafon)
// La fiecare `--interval` minute: un cap de jurnal nou daca au venit capete, si notarizarea capetelor de jurnal nenotarizate (ultimul al fiecarui
// ciob pastrat) pe AereNotary 28001, cu o cheie de PROBA a testnetului (nascuta in starea serviciului daca lipseste, alimentata din robinet daca
// e dat). Orice alt lant decat 28001 e refuzat inainte de cheie: mainnet-ul cere tranzactiile fondatorului ("sandbox: 28001; mainnet: at activation").
// Ce tine jurnalul de cereri: momentul, ruta (fara id-uri) si statusul; o eroare interna merge INTREAGA numai aici, raspunsul public poarta un cod
// (revizia B3, constatarea 18). Limitele de ritm stau in memorie, cu cheile sarate pe proces, fiecare cu un plafon de chei.
//
// node martor-agenti-serviciu.mjs --stare <dir> [--port 8802] [--interval 10] [--rpc https://testnet-rpc.aere.network] [--cheie <f>]
// [--robinet <robinet.mjs>] [--log aere-agent-witness/28001] [--merge-max 1800] [--fara-lant] [--o-data]
// --fara-lant laborator: capete de jurnal construite si semnate, nicio notarizare (chitantele raman `checkpointed`)
// --o-data un singur tur (cap de jurnal + notarizare), apoi iesire: 0 notarizat sau nimic de facut, 2 notarizarea n-a reusit
import fs from 'node:fs';
import net from 'node:net';
import path from 'node:path';
import http from 'node:http';
import crypto from 'node:crypto';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { deschideMartor, CERERE_MAX_OCTETI, SANDBOX, LANT, CHECKPOINT_KIND, PLAFON_FRUNZE, PAGINA_MAX } from './martor-agenti.mjs';
import { notarizeEnvelope, cheiaDinFisier, NOTARI } from './agent-ancora.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
export const PORT_IMPLICIT = 8802;
export const RPC_28001 = 'https://testnet-rpc.aere.network';
// limitele, pe fereastra de 10 minute: un agent cinstit trimite un cap la cateva minute; GET-urile sunt ieftine dar nu nelimitate. Bugetul
// jurnalului (capete NOI primite pe ora, de la toti) e DERIVAT din plafonul ciobului: PLAFON_FRUNZE / 24 pe ora, deci un ciob nu se umple in mai
// putin de o zi, iar un cap al lui se serveste cel putin o zi dupa ce ciobul e inghetat (martorul tine ciobul curent si pe cel de dinainte).
export const LIMITE = { postPeClient: { cereri: 60, ms: 600_000 }, postPeAgent: { cereri: 30, ms: 600_000 }, getPeClient: { cereri: 1200, ms: 600_000 },
acceptatePeOra: { cereri: Math.floor(PLAFON_FRUNZE / 24), ms: 3_600_000 } };
// plafonul de chei al unui limitator, DERIVAT din masuratoare (2026-10-19, README "Bounds"): 198 de octeti pe cheie (100.000 de chei = 19,8 MB pe
// limitator, deci ~60 MB pentru cele trei). La plafon (dupa curatarea cheilor expirate) se scoate cheia cu fereastra cea mai VECHE si clientul nou
// intra (revizia B3 a doua, R2-03): prima reparatie limita aici orice client NOU, deci cine umplea 100.000 de chei (de pe /64-uri reale) tinea
// afara pe toti clientii noi cat le reimprospata. Un client scos asa primeste doar o fereastra noua; un atacator care are deja 100.000 de chei are
// deja 100.000 de cote. Memoria nu creste cu adresele (pana la prima reparatie: 293 de octeti pe adresa, pe viata procesului, si niciun plafon).
export const LIMITATOR_MAX_CHEI = 100_000;
const taie = (s) => String(s ?? '').replace(/(0x)?[0-9a-fA-F]{40,}/g, '<hex>').slice(0, 300);
/**
* Cheia unui client pentru limite (revizia B3, constatarea 13): o adresa IPv6 pe prefixul ei /64 (un client IPv6 primeste de obicei un /64 intreg,
* deci pe adresa intreaga ar fi avut 2^64 de chei), o adresa IPv4 mapata in IPv6 ca IPv4, o adresa IPv4 intreaga; orice altceva (o proba) ca atare.
*/
export function cheieClient(a) {
let s = String(a ?? '').trim().toLowerCase();
if (s.startsWith('[') && s.includes(']')) s = s.slice(1, s.indexOf(']'));
const pct = s.indexOf('%'); if (pct >= 0) s = s.slice(0, pct);
const m4 = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(s); if (m4 && net.isIPv4(m4[1])) return m4[1];
if (net.isIPv4(s)) return s;
if (net.isIPv6(s)) {
let [cap, coada = ''] = s.split('::'); const g = (t) => (t ? t.split(':') : []);
let a1 = g(cap), a2 = g(coada);
const v4 = (l) => { const u = l[l.length - 1]; if (u && u.includes('.')) { const o = u.split('.').map(Number); l.splice(-1, 1, ((o[0] << 8) | o[1]).toString(16), ((o[2] << 8) | o[3]).toString(16)); } };
if (s.includes('::')) v4(a2); else v4(a1);
const tot = s.includes('::') ? [...a1, ...Array(Math.max(0, 8 - a1.length - a2.length)).fill('0'), ...a2] : a1;
return tot.slice(0, 4).map((x) => (parseInt(x, 16) || 0).toString(16)).join(':') + '::/64';
}
return 'altul:' + s.slice(0, 64);
}
/**
* Un limitator pe fereastra fixa (de la prima cerere a cheii), cu cheile sarate pe proces (nicio adresa sau id nu se tine in clar), cu cheile
* expirate CURATATE (la 1024 de apeluri, si cand plafonul e atins, dar cel mult o data pe secunda) si cu un PLAFON de chei: pana la reparatie
* nicio cheie nu se stergea si fiecare adresa noua lasa ~293 de octeti pe viata procesului (revizia B3, constatarea 13). La plafon se scoate cheia
* cu fereastra cea mai veche (harta e in ordinea inceputului ferestrei: o fereastra noua se muta la coada), numarata (`scoase`), in loc sa fie
* limitat clientul nou (revizia B3 a doua, R2-03). `ar(cheie)`: ar fi limitata cheia acum? fara sa consume nimic (bugetul jurnalului, R2-13).
*/
export function limitator({ cereri, ms }, { maxChei = LIMITATOR_MAX_CHEI, ceas = Date.now } = {}) {
const sare = crypto.randomBytes(16); const m = new Map(); let apeluri = 0, ultimaCuratare = 0, scoase = 0;
const cheieDe = (cheie) => crypto.createHash('sha256').update(sare).update(String(cheie)).digest('base64url').slice(0, 16);
const curata = (acum) => { ultimaCuratare = acum; for (const [k, v] of m) if (acum - v[0] >= ms) m.delete(k); };
const f = (cheie) => {
const k = cheieDe(cheie);
const acum = ceas();
if (++apeluri % 1024 === 0) curata(acum);
let v = m.get(k);
if (v && acum - v[0] >= ms) { m.delete(k); v[0] = acum; v[1] = 0; m.set(k, v); }
if (!v) {
if (m.size >= maxChei && acum - ultimaCuratare >= 1000) curata(acum);
if (!(m.size < maxChei /*PAZA-MARTOR:ritm-plafon*/)) { m.delete(m.keys().next().value); scoase++; }
v = [acum, 0]; m.set(k, v);
}
if (v[1] >= cereri) return true;
v[1]++; return false;
};
f.ar = (cheie) => { const v = m.get(cheieDe(cheie)); return !!v && ceas() - v[0] < ms && v[1] >= cereri; };
// cat mai are de asteptat cheia pana i se deschide o fereastra noua (ms; 0 daca nu e limitata), pentru Retry-After (revizia B3 a cincea, R5-06)
f.ramasMs = (cheie) => { const v = m.get(cheieDe(cheie)); return v && v[1] >= cereri ? Math.max(0, ms - (ceas() - v[0])) : 0; };
f.marime = () => m.size;
f.scoase = () => scoase;
return f;
}
const loopback = (a) => /^(127\.|::1$|::ffff:127\.)/.test(String(a || ''));
/** Serverul HTTP peste starea deschisa `m` (deschideMartor). Clientul real e X-Real-IP numai cand cererea vine de pe loopback (gateway-ul il pune). */
export function porneste(m, { port = 0, limite = LIMITE, jurnal = () => {}, maxChei = LIMITATOR_MAX_CHEI } = {}) {
const peClient = limitator(limite.postPeClient, { maxChei }), peAgent = limitator(limite.postPeAgent, { maxChei }), citiri = limitator(limite.getPeClient, { maxChei });
const acceptate = limite.acceptatePeOra ? limitator(limite.acceptatePeOra, { maxChei: 1 }) : null;
// bugetul jurnalului: martorul il CITESTE intai (`ar`, fara sa consume) si il consuma abia dupa cota agentului (R2-13)
const limitaGlobala = acceptate ? Object.assign(() => acceptate('jurnal'), { ar: () => acceptate.ar('jurnal') /*PAZA-MARTOR:buget-citit*/ }) : null;
// starea limitatoarelor, in /health: cate chei tin si cate au scos la plafon (o inundatie din multe adrese se vede, nu se ghiceste)
const stareLimite = () => Object.fromEntries([['postPerClient', peClient], ['postPerAgent', peAgent], ['getPerClient', citiri]].map(([n, l]) => [n, { keys: l.marime(), maxKeys: maxChei, droppedAtCap: l.scoase() }]));
const raspunde = (req, res, cod, obiect, antete = {}) => {
const text = Buffer.from(JSON.stringify(obiect));
res.writeHead(cod, { 'content-type': 'application/json; charset=utf-8', 'content-length': text.length, 'cache-control': 'no-store', 'x-content-type-options': 'nosniff', ...antete });
res.end(text);
jurnal(`${req.method} ${String(req.url).replace(/0x[0-9a-fA-F]{64}/g, '0x..').replace(/aere-agent:[0-9a-f]{40}/g, 'aere-agent:..').replace(/\?.*$/, '').slice(0, 60)} ${cod}`);
};
// o eroare interna: INTREAGA numai in jurnalul local; public un cod (fara cai, fara numele starii)
const interna = (req, res, e) => { jurnal(`internal error on ${req.method} ${String(req.url).replace(/\?.*$/, '').slice(0, 60)}: ${taie(e && (e.stack || e.message))}`); if (!res.headersSent) raspunde(req, res, 500, { error: 'witness_error', code: e && /^E[A-Z]+$/.test(String(e.code)) ? 'io_error' : 'internal', sandbox: SANDBOX }); };
const RUTE = ['POST /v1/agents/witness', 'GET /v1/agents/witness/0x<head id>', 'GET /v1/agents/witness/aere-agent:<40 hex>/heads[?session=<32 hex>&from=<seq>&limit=<1..100>][?after=<session>]', 'GET /v1/agents/witness/equivocation/0x<id>', 'GET /v1/agents/witness/checkpoint[?shard=<k>][&from=<size>]', 'GET /v1/agents/witness/health'];
const intreg = (s, d) => (s === null ? d : /^\d{1,15}$/.test(s) ? Number(s) : NaN);
const handler = (req, res) => {
try {
const url = new URL(req.url, 'http://x'); const cale = url.pathname;
const client = cheieClient(loopback(req.socket.remoteAddress) && req.headers['x-real-ip'] ? String(req.headers['x-real-ip']) : String(req.socket.remoteAddress || ''));
if (req.method === 'POST' && cale === '/v1/agents/witness') {
if (peClient(client) /*PAZA-MARTOR:ritm*/) return raspunde(req, res, 429, { error: 'rate_limited', hint: `${limite.postPeClient.cereri} heads per ${limite.postPeClient.ms / 60000} minutes per client`, sandbox: SANDBOX });
const ct = String(req.headers['content-type'] || '').split(';')[0].trim().toLowerCase();
const parti = []; let n = 0, prea = false;
req.on('data', (d) => { n += d.length; if (n > CERERE_MAX_OCTETI) { if (!prea) { prea = true; raspunde(req, res, 413, { error: 'body_too_large', max: CERERE_MAX_OCTETI, hint: 'a signed head is about 8 KB; the witness never takes the ledger', sandbox: SANDBOX }); req.destroy(); } } else parti.push(d); });
req.on('end', () => {
if (prea) return;
try {
if (ct !== 'application/json') return raspunde(req, res, 415, { error: 'bad_content_type', hint: 'application/json: the signed head (martor-agenti.mjs request)', sandbox: SANDBOX });
let env; try { env = JSON.parse(Buffer.concat(parti).toString('utf8')); } catch { return raspunde(req, res, 400, { error: 'bad_json', sandbox: SANDBOX }); }
const r = m.primeste(env, { limitaAgent: peAgent, limitaGlobala });
return raspunde(req, res, r.cod, r.corp, r.corp && r.corp.retryAfterS ? { 'retry-after': String(r.corp.retryAfterS) } : {});
} catch (e) { return interna(req, res, e); }
});
req.on('error', () => {});
return;
}
if (req.method !== 'GET') return raspunde(req, res, 405, { error: 'method_not_allowed', routes: RUTE, sandbox: SANDBOX });
// o citire peste limita spune si cand se poate reincerca (Retry-After, secundele pana la fereastra noua a clientului): verificatorul aduce
// cate o chitanta pe cap tinut si asteapta cat spune antetul (revizia B3 a cincea, R5-06)
if (citiri(client)) { const ra = Math.max(1, Math.ceil(citiri.ramasMs(client) / 1000)); return raspunde(req, res, 429, { error: 'rate_limited', retryAfterS: ra, sandbox: SANDBOX }, { 'retry-after': String(ra) } /*PAZA-MARTOR:get-retry-after*/); }
if (cale === '/v1/agents/witness/health') return raspunde(req, res, 200, { ...m.sanatate(), limiters: stareLimite() });
if (cale === '/v1/agents/witness/checkpoint') {
const sh = url.searchParams.get('shard'); if (sh !== null && !/^\d{1,9}$/.test(sh)) return raspunde(req, res, 400, { error: 'bad_shard', sandbox: SANDBOX });
const r = m.capulJurnalului(url.searchParams.get('from'), sh === null ? null : Number(sh)); return raspunde(req, res, r.cod, r.corp);
}
const me = /^\/v1\/agents\/witness\/equivocation\/(0x[0-9a-fA-F]{64})$/.exec(cale);
if (me) { const r = m.dovadaEchivocarii(me[1]); return raspunde(req, res, r.cod, r.corp); }
const mi = /^\/v1\/agents\/witness\/(0x[0-9a-fA-F]{64})$/.exec(cale);
if (mi) { const r = m.stareaCapului(mi[1]); return raspunde(req, res, r.cod, r.corp); }
const ma = /^\/v1\/agents\/witness\/(aere-agent:[0-9a-f]{40})\/heads$/.exec(cale);
if (ma) {
const s = url.searchParams.get('session'), after = url.searchParams.get('after');
const from = intreg(url.searchParams.get('from'), 0), limit = intreg(url.searchParams.get('limit'), PAGINA_MAX);
if (s !== null && !/^[0-9a-f]{32}$/.test(s)) return raspunde(req, res, 400, { error: 'bad_session', sandbox: SANDBOX });
if (after !== null && !/^[0-9a-f]{32}$/.test(after)) return raspunde(req, res, 400, { error: 'bad_after', sandbox: SANDBOX });
if (!(Number.isSafeInteger(from) && from >= 0 && Number.isSafeInteger(limit) && limit >= 1 && limit <= PAGINA_MAX)) return raspunde(req, res, 400, { error: 'bad_page', hint: `from >= 0, 1 <= limit <= ${PAGINA_MAX}`, sandbox: SANDBOX });
return raspunde(req, res, 200, m.capeteAgent(ma[1], s, { from, limit, after }));
}
return raspunde(req, res, 404, { error: 'not_found', routes: RUTE, sandbox: SANDBOX });
} catch (e) { return interna(req, res, e); }
};
const server = http.createServer(handler);
return new Promise((ok) => server.listen(port, '127.0.0.1', () => ok({ server, port: server.address().port, close: () => new Promise((g) => server.close(() => g())) })));
}
/**
* Notarizatorul real: capul de jurnal pe AereNotary, NUMAI pe testnetul 28001 (lantul citit de la RPC, refuzat altfel inainte de cheie), cu o
* cheie de proba din fisier (nascuta acolo, 0600, daca lipseste; niciodata tiparita), alimentata din robinetul testnetului cand soldul nu
* ajunge si cand `robinet` numeste clientul lui (tools/aere-quantum-shield/robinet.mjs; fara el, `unfunded`, spus). Intoarce {ok, notarization}
* sau {ok: false, cod, motiv}: `cod` e cel public (unfunded, chain_refused, ...), `motiv` merge numai in jurnalul local.
*/
export function notarizatorReal({ rpc = RPC_28001, cheieFisier, robinet = null, ethers = null }) {
return async (env) => {
let E = ethers;
if (!E) { try { const { incarcaEthers } = await import(pathToFileURL(path.join(AICI, 'x402', 'wallet.mjs')).href); E = incarcaEthers(); } catch (e) { return { ok: false, cod: 'ethers_missing', motiv: 'ethers is not here: ' + taie(e.message) }; } }
try {
const provider = new E.JsonRpcProvider(rpc);
const chainId = Number((await provider.getNetwork()).chainId);
if (!(chainId === LANT /*PAZA-MARTOR:sandbox*/)) return { ok: false, cod: 'chain_refused', motiv: `REFUSED: this RPC serves chain ${chainId}; the agents witness notarizes on the public testnet ${LANT} only (mainnet at activation, with the founder's transactions)` };
if (!fs.existsSync(cheieFisier)) {
const w = E.Wallet.createRandom();
fs.mkdirSync(path.dirname(cheieFisier), { recursive: true });
fs.writeFileSync(cheieFisier, `# AERE-SINTETIC: test key of the agents witness for testnet ${LANT}, made ${new Date().toISOString()}; funds only from the faucet\nd=${w.privateKey.slice(2)}\n`, { mode: 0o600, flag: 'wx' });
}
const wallet = new E.Wallet(cheiaDinFisier(cheieFisier), provider);
const min = E.parseEther('0.01');
if ((await provider.getBalance(wallet.address)) < min) {
if (!robinet) return { ok: false, cod: 'unfunded', motiv: 'unfunded: the test key has no testnet balance and no faucet client was given (--robinet)' };
const { cereRobinet } = await import(pathToFileURL(path.resolve(robinet)).href);
const c = await cereRobinet(wallet.address);
if (!c.ok) return { ok: false, cod: 'unfunded', motiv: 'unfunded: the faucet refused: ' + taie(JSON.stringify(c.claim || c.motiv)) };
let sold = 0n; for (let i = 0; i < 40 && sold < min; i++) { await new Promise((g) => setTimeout(g, 1500)); sold = await provider.getBalance(wallet.address); }
if (sold < min) return { ok: false, cod: 'unfunded', motiv: 'unfunded: the faucet transaction did not land in 60 s' };
}
const r = await notarizeEnvelope({ envelope: env, kind: CHECKPOINT_KIND, rpc, keyFile: cheieFisier, ethers: E, notary: NOTARI[LANT], confirmMainnet: false });
return r.ok ? { ok: true, notarization: { ...r.envelope.notarization, submitter: wallet.address } } : { ok: false, cod: 'notarization_failed', motiv: taie(r.motiv) };
} catch (e) { return { ok: false, cod: 'notarization_failed', motiv: 'the notarization failed: ' + taie(e.shortMessage || e.message) }; }
};
}
/** Un tur: un cap de jurnal nou (daca au venit capete), apoi notarizarea fiecarui cap de jurnal nenotarizat (cel final al unui ciob inghetat intai). */
export async function tur(m, { notarizeaza = null } = {}) {
const nou = m.construiestePunct();
const deFacut = m.deNotarizate();
if (!deFacut.length) return { built: nou ? nou.n : null, notarized: null, note: nou ? 'built' : 'nothing new' };
if (!notarizeaza) return { built: nou ? nou.n : null, notarized: null, note: 'laboratory: no notarization' };
let ultim = null;
for (const p of deFacut) {
const r = await notarizeaza(m.plicPunct(p));
m.inregistreazaNotarizarea(p, r);
if (!r.ok) return { built: nou ? nou.n : null, notarized: ultim ? ultim.n : null, shard: p.k, firstSeen: null, error: r.motiv, code: r.cod || 'notarization_failed' };
ultim = { n: p.n, k: p.k, firstSeen: r.notarization.firstSeen };
}
return { built: nou ? nou.n : null, notarized: ultim.n, shard: ultim.k, firstSeen: ultim.firstSeen, error: null };
}
// ------------------------------------------------------------------------------------------------ CLI
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await main();
async function main() {
const arg = (n, d) => { const i = process.argv.indexOf(n); return i > 0 ? process.argv[i + 1] : d; };
const stare = arg('--stare', null);
if (!stare) { console.error('usage: martor-agenti-serviciu.mjs --stare <dir> [--port 8802] [--interval 10] [--rpc url] [--cheie f] [--robinet robinet.mjs] [--log name] [--merge-max s] [--fara-lant] [--o-data]'); process.exitCode = 2; return; }
const scrieJurnal = (l) => console.log(new Date().toISOString() + ' ' + l);
let m;
const faraLant = process.argv.includes('--fara-lant');
// laboratorul (--fara-lant) nu notarizeaza nimic: rotirea nu asteapta notarizarea capului de jurnal final al ciobului scos (R2-07)
try { m = deschideMartor(stare, { log: arg('--log', undefined), maxMergeDelay: arg('--merge-max', null) != null ? Number(arg('--merge-max')) : undefined, jurnal: scrieJurnal, faraLant }); }
catch (e) { console.error('cannot open the state: ' + taie(e.message)); process.exitCode = 2; return; }
const notarizeaza = faraLant ? null : notarizatorReal({ rpc: arg('--rpc', RPC_28001), cheieFisier: arg('--cheie', path.join(stare, 'cheie-de-proba-28001.key')), robinet: arg('--robinet', null) });
const t = async () => { try { const r = await tur(m, { notarizeaza }); console.log(new Date().toISOString() + ' round: ' + JSON.stringify(r).slice(0, 300)); return r; } catch (e) { console.log(new Date().toISOString() + ' round UNMEASURED: ' + taie(e.message)); return { error: e.message }; } };
if (process.argv.includes('--o-data')) { const r = await t(); m.inchide(); process.exitCode = r.error ? 2 : 0; return; }
const srv = await porneste(m, { port: Number(arg('--port', PORT_IMPLICIT)), jurnal: scrieJurnal });
console.log(`agents witness ${m.witnessId} on 127.0.0.1:${srv.port} (log ${m.log}, state ${stare}, ${faraLant ? 'laboratory: no chain' : 'notarizing on chain ' + LANT}); ${SANDBOX}`);
const opreste = () => { m.inchide(); srv.close().then(() => process.exit(0)); };
process.on('SIGTERM', opreste); process.on('SIGINT', opreste);
await t(); setInterval(t, Number(arg('--interval', 10)) * 60_000);
}

1129
agents/martor-agenti.mjs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -116,9 +116,9 @@ issuer and for its status at once.
## The vectors
`vectori/` holds 631 vectors (622 offline, 9 that read testnet 28001; 22 MB, since every device, identity and Travel Rule vector carries its ML-DSA-65 keys and signatures), listed with their digests in `vectori/INDEX.json`:
envelope 67, payment-risk 11, payment-receipt 10, market-timeline 26, dataset 22, ai-trust-bundle 24, audit-log 19, audit-inclusion
10, audit-consistency 4, agent-ledger 18, identity-presentation 69, device-bundle 194, sdjwt-presentation 59, sdjwt-vc-presentation 32, travel-rule-binding 19, travel-rule-message 26, travel-rule-receipt 9, compliance-presentation 12. By expected verdict: 89 `VALID`, 70 `PARTIAL`, 472 `INVALID`. None is typed by hand
`vectori/` holds 634 vectors (625 offline, 9 that read testnet 28001; 22 MB, since every device, identity and Travel Rule vector carries its ML-DSA-65 keys and signatures), listed with their digests in `vectori/INDEX.json`:
envelope 70 (since 2026-10-19: 67 plus the three of the new kind `agent-witness-checkpoint`), payment-risk 11, payment-receipt 10, market-timeline 26, dataset 22, ai-trust-bundle 24, audit-log 19, audit-inclusion
10, audit-consistency 4, agent-ledger 18, identity-presentation 69, device-bundle 194, sdjwt-presentation 59, sdjwt-vc-presentation 32, travel-rule-binding 19, travel-rule-message 26, travel-rule-receipt 9, compliance-presentation 12. By expected verdict: 90 `VALID`, 70 `PARTIAL`, 474 `INVALID`. None is typed by hand
(`node genereaza-vectori.mjs --verifica` regenerates them and compares byte for byte):
- the 17 AIP-23 kinds come from their producers on fixed inputs (the same envelopes as the published AIP-23 kind vectors; `secret-access`, the audit entry of the secrets service, since 2026-10-18); the audit
@ -225,6 +225,18 @@ not implementations of this suite's contract).
## How the suite itself is checked
Measured on 2026-10-19 (UTC), after the AIP-23 kind `agent-witness-checkpoint` was added (the checkpoint of the hosted agents witness,
`agents/martor-agenti.mjs`; three envelope vectors: VALID, its statementHash cut short, one statement field changed; no rule changed). On
Node.js 24.14.1, offline, with the development reference verifier: `node proba-conformance.mjs` gives 12 rows green, 0 red, 1 not measured
(the 9 vectors that read testnet 28001): 241 rules of 15 verifiers, 634 vectors (`LA ZI`), the 18 kind envelopes are the AIP-23 kind
vectors, every INVALID vector (474) is its base with one operation (308) or one declared producer change (166), the published verifiers
pass 625 of 625 offline vectors, the independent Python implementation passes the 68 it claims and reports the other 566 not covered,
101,947 malformed inputs each got a verdict. Not run after this change: `--online` and `--control-negativ`.
Measured again the same day after the kind gained a vocabulary in `proof-kinds/` (the adversarial review of the hosted witness, finding 8;
the valid inputs build byte for byte the same envelopes): `genereaza-vectori.mjs --verifica` `LA ZI` (634 vectors), `proba-conformance.mjs`
12 green, 0 red, 1 not measured (the testnet vectors), and `--control-negativ` 320 green, 0 red, 3 not measured (the three rules whose
vectors need testnet 28001), 238 of 241 rules proven load-bearing. `--online` not run.
Measured on 2026-10-19 (UTC), after the SD-JWT verifier gained the rule `hybrid-half` (AIP-25 finding C1, D-535: with the keys of an
AERE hybrid identity the ML-DSA-65 half is judged by default and the Ed25519 half only when the verifier asks for it, `classicalHalf`;
until then the Ed25519 half was the default, so a token signed with it alone verified against the whole hybrid identity). Five vectors

View File

@ -600,7 +600,9 @@ function intrariKinds() {
settlement: { chainId: 2800, txHash: '0x' + '12'.repeat(32), blockHash: '0x' + '34'.repeat(32), from: '0xa', to: '0xb', amount: '100', asset: 'AERE', instruction: 'INSTR', createdAt: AT },
provenance: { subject: 'ARTIFACT', parents: ['SRC-1', 'SRC-2'], process: 'BUILD', actor: 'build-bot', createdAt: AT },
// 2026-10-18 (B8, cloud-gateway/pq-secrets): the same fixed input as the published kind vector (aere-proof-protocol/genereaza-vectori-kinds.mjs)
'secret-access': { service: 'https://secrets.example', secret: DB, version: 2, operation: 'get', verdict: 'allowed', rules: [], grant: 'deployers', holder: 'aere-id:' + '0'.repeat(40), presenter: 'aere-id:' + '1'.repeat(40), presentation: 'BINDING', policy: 'POLICY', createdAt: AT } };
'secret-access': { service: 'https://secrets.example', secret: DB, version: 2, operation: 'get', verdict: 'allowed', rules: [], grant: 'deployers', holder: 'aere-id:' + '0'.repeat(40), presenter: 'aere-id:' + '1'.repeat(40), presentation: 'BINDING', policy: 'POLICY', createdAt: AT },
// 2026-10-19 (B3, tools/agent-policy/martor-agenti.mjs): the same fixed input as the published kind vector
'agent-witness-checkpoint': { root: '0x' + '5a'.repeat(32), previous: '0x' + '6b'.repeat(32), log: 'aere-agent-witness/28001', witness: 'aere-witness:' + '7c'.repeat(20), chainId: 28001, treeSize: 3, maxMergeDelay: 1800, createdAt: AT } };
}
/** Every AIP-23 kind, from its producer, on the fixed inputs. */
export function plicuriKinds() {

View File

@ -1,9 +1,9 @@
{
"v": 1,
"kind": "aere-conformance-vector-index",
"count": 631,
"count": 634,
"artifacts": {
"envelope": 67,
"envelope": 70,
"payment-risk": 11,
"payment-receipt": 10,
"market-timeline": 26,
@ -25,7 +25,7 @@
"dataset-consistency": 3,
"dataset-sample": 2
},
"offline": 622,
"offline": 625,
"online": 9,
"rules": {
"agents.anchor": [
@ -516,6 +516,7 @@
"envelope/settlement.form",
"envelope/provenance.form",
"envelope/secret-access.form",
"envelope/agent-witness-checkpoint.form",
"envelope/ai.form",
"envelope/agent-decision.form",
"envelope/data.form-null",
@ -542,6 +543,7 @@
"envelope/settlement.integrity",
"envelope/provenance.integrity",
"envelope/secret-access.integrity",
"envelope/agent-witness-checkpoint.integrity",
"envelope/ai.integrity",
"envelope/agent-decision.integrity",
"market-timeline/s1.proof-other-result"
@ -1325,6 +1327,27 @@
"requires": [],
"sha256": "0x8c1f6f75345a65444348e01f9a95932cf62e0168b5e55fdb13934d306ad3556a"
},
{
"id": "envelope/agent-witness-checkpoint.valid",
"artifact": "envelope",
"verdict": "VALID",
"requires": [],
"sha256": "0x2362b0c00f7c393239807762d91d7a56d722c95acd3850e121cb5e7c12ff1fa3"
},
{
"id": "envelope/agent-witness-checkpoint.form",
"artifact": "envelope",
"verdict": "INVALID",
"requires": [],
"sha256": "0x1155afce20406b5105abdbeec4ccc1082ebf6f69c5d5399eede84efa5c82e1d9"
},
{
"id": "envelope/agent-witness-checkpoint.integrity",
"artifact": "envelope",
"verdict": "INVALID",
"requires": [],
"sha256": "0x81463e18a9109d6020b0dfdba2883252e5b4241a9ae02007a3bd8926bf4c8849"
},
{
"id": "envelope/ai.valid",
"artifact": "envelope",

View File

@ -0,0 +1,53 @@
{
"v": 1,
"kind": "aere-conformance-vector",
"id": "envelope/agent-witness-checkpoint.form",
"artifact": "envelope",
"title": "agent-witness-checkpoint: the statementHash is 63 hex digits",
"source": "derived from envelope/agent-witness-checkpoint.valid",
"requires": [],
"exercises": [
"envelope.form",
"envelope.integrity"
],
"base": "envelope/agent-witness-checkpoint.valid",
"mutation": {
"op": "set",
"path": [
"envelope",
"statementHash"
],
"value": "0x52c07602afdc773b6b2441b5899161a192b973ad4c9fcad52f6f4c1ef3ca2ac"
},
"input": {
"envelope": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint",
"root": "0x5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"previous": "0x6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b",
"log": "aere-agent-witness/28001",
"witness": "aere-witness:7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c",
"chainId": 28001,
"treeSize": 3,
"maxMergeDelay": 1800,
"createdAt": "2026-09-26T00:00:00Z"
},
"statementHash": "0x52c07602afdc773b6b2441b5899161a192b973ad4c9fcad52f6f4c1ef3ca2ac"
}
},
"expected": {
"verdict": "INVALID",
"failed": [
"envelope.form"
],
"levels": {
"integrity": "FAILED",
"authenticity": "NOT_MEASURED",
"anchoring": "NOT_MEASURED",
"finality": "NOT_MEASURED"
}
}
}

View File

@ -0,0 +1,54 @@
{
"v": 1,
"kind": "aere-conformance-vector",
"id": "envelope/agent-witness-checkpoint.integrity",
"artifact": "envelope",
"title": "agent-witness-checkpoint: statement.root changed after hashing",
"source": "derived from envelope/agent-witness-checkpoint.valid",
"requires": [],
"exercises": [
"envelope.form",
"envelope.integrity"
],
"base": "envelope/agent-witness-checkpoint.valid",
"mutation": {
"op": "set",
"path": [
"envelope",
"statement",
"root"
],
"value": "0x5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a0"
},
"input": {
"envelope": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint",
"root": "0x5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a0",
"previous": "0x6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b",
"log": "aere-agent-witness/28001",
"witness": "aere-witness:7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c",
"chainId": 28001,
"treeSize": 3,
"maxMergeDelay": 1800,
"createdAt": "2026-09-26T00:00:00Z"
},
"statementHash": "0x52c07602afdc773b6b2441b5899161a192b973ad4c9fcad52f6f4c1ef3ca2aca"
}
},
"expected": {
"verdict": "INVALID",
"failed": [
"envelope.integrity"
],
"levels": {
"integrity": "FAILED",
"authenticity": "NOT_MEASURED",
"anchoring": "NOT_MEASURED",
"finality": "NOT_MEASURED"
}
}
}

View File

@ -0,0 +1,44 @@
{
"v": 1,
"kind": "aere-conformance-vector",
"id": "envelope/agent-witness-checkpoint.valid",
"artifact": "envelope",
"title": "the agent-witness-checkpoint envelope from its producer",
"source": "producer on fixed inputs",
"requires": [],
"exercises": [
"envelope.form",
"envelope.integrity"
],
"base": null,
"mutation": null,
"input": {
"envelope": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint-attestation",
"statement": {
"v": 1,
"kind": "aere-proof-of-agent-witness-checkpoint",
"root": "0x5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"previous": "0x6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b6b",
"log": "aere-agent-witness/28001",
"witness": "aere-witness:7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c",
"chainId": 28001,
"treeSize": 3,
"maxMergeDelay": 1800,
"createdAt": "2026-09-26T00:00:00Z"
},
"statementHash": "0x52c07602afdc773b6b2441b5899161a192b973ad4c9fcad52f6f4c1ef3ca2aca"
}
},
"expected": {
"verdict": "VALID",
"failed": [],
"levels": {
"integrity": "PASSED",
"authenticity": "NOT_MEASURED",
"anchoring": "NOT_MEASURED",
"finality": "NOT_MEASURED"
}
}
}

View File

@ -1,8 +1,18 @@
// scaneaza-local.mjs <port>: scanerul AERE de pregatire post-cuantica (cloud-gateway/readiness-service.mjs, functia scaneazaAdresa,
// fara garda de adrese private: tinta e 127.0.0.1, aleasa de noi) pe serverul de proba al unei retete. Tipareste verdictul, rezumatul si
// constatarile, exact ce ar vedea un client al scanerului pe serverul lui dupa aplicarea retetei.
import { pathToFileURL } from 'node:url';
import fs from 'node:fs';
import { pathToFileURL, fileURLToPath } from 'node:url';
const port = Number(process.argv[2] || 8445);
const { scaneazaAdresa } = await import(pathToFileURL('C:/Users/Jessica/Documents/AereNetwork/aerenew/cloud-gateway/readiness-service.mjs').href);
// 2026-10-19: scanerul se ia relativ la acest fisier, in ambele asezari (cloud-gateway/readiness-service.mjs in arborele de dezvoltare,
// readiness/readiness-service.mjs in copia publica aere-quantum), sau din AERE_READINESS_SERVICE; prima forma purta calea absoluta de pe
// laptopul de lucru, publicata asa in aere-quantum (D-541).
const CAND = [new URL('../../../readiness-service.mjs', import.meta.url), new URL('../../../readiness/readiness-service.mjs', import.meta.url)].find((u) => fs.existsSync(fileURLToPath(u)));
const SCANER = process.env.AERE_READINESS_SERVICE ? pathToFileURL(process.env.AERE_READINESS_SERVICE).href : CAND && CAND.href;
if (!SCANER) { console.error('scaneaza-local.mjs: scanerul de pregatire (readiness-service.mjs) nu se gaseste nici in arborele de dezvoltare, nici in copia publica; da calea in AERE_READINESS_SERVICE'); process.exit(2); }
const { scaneazaAdresa } = await import(SCANER).catch((e) => {
console.error('scaneaza-local.mjs: scanerul de pregatire nu s-a putut incarca (' + e.message + ')');
process.exit(2);
});
const r = await scaneazaAdresa('proba.local', { address: '127.0.0.1', family: 4 }, { port, jurnal: false });
console.log(JSON.stringify({ score: r.score, verdict: r.verdict, summary: r.summary, findings: (r.findings || []).map((f) => f.id + ':' + f.severity), error: r.error, detail: r.detail }, null, 1).replace(/\n\s*/g, ' '));

View File

@ -68,6 +68,13 @@ tool does not guess. At most 64 distinct variables are resolved per file; past t
file are reported as `unknown`, and the file is counted (`files-over-resolution-limit` in the CBOM, a
line in the text summary).
The TLS protocol version of a Node.js option (`minVersion`, `maxVersion`, `tls.DEFAULT_MIN_VERSION`, `tls.DEFAULT_MAX_VERSION`) is
resolved the same way since 0.4.1 (2026-10-19): a constant holding a version string is followed, and a constant assigned a
conditional between two version strings (`const min = strict ? 'TLSv1.3' : 'TLSv1.2'`) is reported with the WEAKER of the two,
since that is the version the code can end up allowing; `resolvedFrom` names the line and says it was a conditional. Any other
expression gives an `unknown` finding. Before 0.4.1 a version given through a variable produced no TLS finding at all, not even an
`unknown` one; it was found on our own TLS gateway, whose upstream minimum had moved into such a constant.
## Cost on hostile input
The scanner reads repositories it did not write, so its cost is kept linear in the size of each file,
@ -215,7 +222,7 @@ console.log(renderSummary(result));
```
node test/proba.mjs # 68 probes: fixtures per language with the exact expected findings
node test/control-negativ.mjs # breaks the scanner in a copy, one rule at a time (35 mutations), and
node test/control-negativ.mjs # breaks the scanner in a copy, one rule at a time (38 mutations), and
# requires the matching probe to fail while the rest of the suite still runs
node test/proba-timp.mjs # 13 hostile inputs, each at two sizes: the cost must grow linearly
node test/control-negativ-timp.mjs # puts each quadratic form back (and one Swift comment rescan) and requires the cost probe to turn red

View File

@ -182,6 +182,27 @@ function rezolvaOData(ctx, nume) {
return null;
}
// 0.4.1 (2026-10-19, D-540): un TERNAR intre doua literale, `const X = <conditie> ? 'A' : 'B'`, cu aceleasi conditii ca rezolva()
// (exact o atribuire in fisier, pe randul declaratiei, numele nu e parametru). Intoarce ambele valori: cine il cheama alege ce conteaza
// (pentru o versiune TLS, ramura cea mai slaba). Numai JavaScript/TypeScript; orice alta forma: null.
export function rezolvaTernarJs(ctx, nume) {
if (ctx.limbaj !== 'javascript' || !identificator(nume)) return null;
const c = ctx.code;
const e = escapeRe(nume);
const atribuiri = ctx.potriviri(new RegExp(`(?<![\\w$.])${e}\\s*(?:[-+*/%|&^]|\\?\\?|\\|\\||&&)?(?::=|=)(?![=>])`, 'g'));
// garda proprie, cu alt text decat cea din rezolvaOData: controlul negativ le ancoreaza separat
if (atribuiri.length > 1 || !atribuiri.length) return null;
const k = atribuiri[0].index;
const ls0 = c.lastIndexOf('\n', k) + 1;
const le0 = c.indexOf('\n', k) < 0 ? c.length : c.indexOf('\n', k);
const linie = c.slice(ls0, le0);
const parametru = new RegExp(`(?:function\\b[^(]{0,200}\\(|\\()([^()]{0,2000}(?<![\\w$.])${e}(?![\\w$])[^()]{0,2000})\\)\\s*(?:=>|\\{)`).test(c)
|| new RegExp(`\\bfor\\s*\\(\\s*(?:const|let|var)?\\s*${e}\\s+(?:of|in)\\b`).test(c)
|| new RegExp(`(?<![\\w$.])${e}\\s*=>`).test(c);
const m = new RegExp(`\\b(?:const|let|var)\\s+${e}\\s*=\\s*[^?\\n;]{1,200}\\?\\s*(['"])([^'"\\n$\\\\]*)\\1\\s*:\\s*(['"])([^'"\\n$\\\\]*)\\3\\s*;?\\s*$`).exec(linie);
return !parametru && m ? { valori: [m[2], m[4]], line: ctx.poz(k).line } : null;
}
// valoarea unui argument: literal, variabila rezolvata sau necunoscuta
export function valoareArgument(ctx, arg) {
if (!arg) return { fel: 'lipsa' };

View File

@ -1,5 +1,5 @@
// Detectorul JavaScript / TypeScript: node:crypto, WebCrypto, optiuni TLS, biblioteci din importuri.
import { argumente, imparteArgumente, valoareArgument, necunoscut, obiectulDin, cifruOpenssl, identificator, rezolva, escapeRe } from './context.mjs';
import { argumente, imparteArgumente, valoareArgument, necunoscut, obiectulDin, cifruOpenssl, identificator, rezolva, rezolvaTernarJs, escapeRe } from './context.mjs';
import { hashCanonic, grupTls, jws, JWS_RE } from './catalog.mjs';
const MODP = { modp1: 768, modp2: 1024, modp5: 1536, modp14: 2048, modp15: 3072, modp16: 4096, modp17: 6144, modp18: 8192 };
@ -210,6 +210,32 @@ function detecteazaTlsJs(ctx) {
const rol = /min/i.test(m[1]) ? 'min' : 'max';
ctx.adauga(m.index, `tls ${m[1]}`, 'config', { grup: 'TLS', param: TLSV[m[3]], rol }, { bucata: m[0] });
}
// 0.4.1 (2026-10-19, D-540): versiunea data printr-o EXPRESIE, nu printr-un sir. Pana la 0.4.0 o constanta (`minVersion: v`) nu dadea
// nicio gasire TLS, nici macar una necunoscuta (gasit pe gateway-ul PQ al casei: `const minSus = ... ? 'TLSv1.3' : 'TLSv1.2'`). Acum, ca la
// ecdhCurve: o constanta se rezolva; un ternar intre doua literale de versiune da ramura cea mai SLABA (pentru o minima e podeaua care
// conteaza, pentru o maxima plafonul cel mai jos), spusa in resolvedFrom; orice alta expresie iese "unknown", nu tace. Un nume de tip
// TypeScript (`SecureVersion`, `string`) dupa doua puncte nu e o valoare.
for (const m of ctx.potriviri(/(?<![\w$])(minVersion|maxVersion|DEFAULT_MIN_VERSION|DEFAULT_MAX_VERSION)\s*(?::|=(?![=>]))\s*/g)) {
const baza = m.index + m[0].length;
const expr = expresiePanaLaVirgula(ctx, baza);
if (!expr || /^(['"])/.test(expr) || /^(?:tls\s*\.\s*)?SecureVersion\b|^string\b/.test(expr)) continue;
const rol = /min/i.test(m[1]) ? 'min' : 'max';
const v = valoareExpresie(ctx, expr);
if (v && TLSV[v.valoare]) {
ctx.adauga(m.index, `tls ${m[1]}`, 'config', { grup: 'TLS', param: TLSV[v.valoare], rol }, { bucata: `${m[1]}: ${v.valoare}`, rezolvatDin: v.din });
continue;
}
const id = identificator(expr);
const ter = id && !v ? rezolvaTernarJs(ctx, id) : null;
if (ter && ter.valori.every((x) => TLSV[x])) {
const ORDINE = ['ssl3', '1.0', '1.1', '1.2', '1.3'];
const slaba = ter.valori.slice().sort((a, b) => ORDINE.indexOf(TLSV[a]) - ORDINE.indexOf(TLSV[b]))[0];
ctx.adauga(m.index, `tls ${m[1]}`, 'config', { grup: 'TLS', param: TLSV[slaba], rol },
{ bucata: `${m[1]}: ${slaba}`, rezolvatDin: `${id} (line ${ter.line}, a conditional: ${ter.valori.join(' or ')}; the weaker one)` });
continue;
}
ctx.adauga(m.index, `tls ${m[1]}`, 'config', necunoscut(expr, `tls ${m[1]}`), { bucata: m[0] + expr });
}
for (const m of ctx.potriviri(/(?<![\w$])secureProtocol\s*:\s*(['"])(\w+)\1/g)) {
const map = { SSLv2_method: 'ssl2', SSLv3_method: 'ssl3', TLSv1_method: '1.0', TLSv1_1_method: '1.1', TLSv1_2_method: '1.2', TLS_method: 'negotiated', SSLv23_method: 'negotiated' };
const v = map[m[2].replace(/_(client|server)_method$/, '_method')];

View File

@ -13,7 +13,7 @@ import { detecteazaKotlin } from './detect-kotlin.mjs';
import { detecteazaPem } from './pem.mjs';
import { citesteManifest, NUME_MANIFEST } from './manifeste.mjs';
export const VERSIUNE = '0.4.0';
export const VERSIUNE = '0.4.1';
export const DOSARE_EXCLUSE_IMPLICIT = ['.git', '.hg', '.svn', 'node_modules', '__pycache__', '.venv', 'venv', '.tox', '.gradle', '.idea', '.mypy_cache', '.pytest_cache', '.claude'];
const LIMBAJ = {

View File

@ -16,6 +16,30 @@ const SUITA = join(AICI, 'proba.mjs');
const B = 'negativ: fisierul binar e sarit si numarat';
const MUTATII = [
{
nume: 'versiunea TLS data printr-o variabila nu mai e citita (D-540: forma de dinainte de 0.4.1)',
fisier: 'lib/detect-js.mjs',
ancora: "if (!expr || /^(['\"])/.test(expr) || /^(?:tls\\s*\\.\\s*)?SecureVersion\\b|^string\\b/.test(expr)) continue;",
cu: "if (!expr || /^(['\"])/.test(expr) || /^(?:tls\\s*\\.\\s*)?SecureVersion\\b|^string\\b/.test(expr) || Boolean.length > 0) continue;",
tinta: 'pozitiv: js/tls.cjs',
martor: B,
},
{
nume: 'ternarul reasignat e rezolvat la prima lui forma (D-540: garda unei singure atribuiri scoasa din rezolvaTernarJs)',
fisier: 'lib/context.mjs',
ancora: "if (atribuiri.length > 1 || !atribuiri.length) return null;",
cu: "if (!atribuiri.length) return null;",
tinta: 'pozitiv: js/tls.cjs',
martor: B,
},
{
nume: 'ternarul versiunii TLS da ramura cea mai TARE, nu cea mai slaba (D-540)',
fisier: 'lib/detect-js.mjs',
ancora: "const slaba = ter.valori.slice().sort((a, b) => ORDINE.indexOf(TLSV[a]) - ORDINE.indexOf(TLSV[b]))[0];",
cu: "const slaba = ter.valori.slice().sort((a, b) => ORDINE.indexOf(TLSV[b]) - ORDINE.indexOf(TLSV[a]))[0];",
tinta: 'pozitiv: js/tls.cjs',
martor: B,
},
{
nume: 'comentariile bloc Rust nu se mai imbrica (lexerul)',
fisier: 'lib/lexer.mjs',

View File

@ -64,7 +64,11 @@ try {
// 0.4.0: si regulile JVM adaugate pentru Kotlin, care se aplica si in Java (Android Keystore, Tink, OkHttp, PKIX, puntea libsignal).
const ADAUGIRI = /"api":"(?:(?:ECKeyPair|IdentityKeyPair|ECPrivateKey|PrivateKey|KEMKeyPair)\.generate|Curve\.generateKeyPair|HKDF\.deriveSecrets|new (?:Aes256GcmSiv|Aes256GcmEncryption|Aes256GcmDecryption|Aes256Ctr32|PreKeyBundle|KEMPublicKey|SealedSessionCipher)|import org\.signal\.libsignal|hkdf|Aes256GcmSiv\.new|PreKeyBundle\.new|processPreKeyBundle|import @signalapp\/libsignal-client(?:\/[^"]*)?|Native\.\w+|KeyGenParameterSpec\.Builder|KeyTemplates\.get|\w+KeyTemplates\.\w+|Predefined\w+Parameters\.\w+|\w+Config\.register|import com\.google\.crypto\.tink|ConnectionSpec\.\w+|CertificatePinner\.Builder|(?:CertPathValidator|TrustManagerFactory)\.getInstance|KeyStore\.getInstance\("AndroidCAStore"\)|X509TrustManager|checkServerTrusted)"/;
const doarV = ka.filter((x) => !sb.has(x)), doarN0 = kb.filter((x) => !sa.has(x));
const adaugate = doarN0.filter((x) => ADAUGIRI.test(x)), doarN = doarN0.filter((x) => !ADAUGIRI.test(x));
// 0.4.1 (2026-10-19, D-540): versiunea TLS data printr-o variabila (rezolvata, sau "unknown"); 0.1.0 nu o vedea deloc. Numai forma noua:
// o gasire a acestei reguli care nu e rezolvata si nu e necunoscuta ar fi forma literala, deci diferenta adevarata.
const ADAUGIRE_TLS = (x) => /"api":"tls (?:minVersion|maxVersion|DEFAULT_MIN_VERSION|DEFAULT_MAX_VERSION)"/.test(x) && (/"resolvedFrom":/.test(x) || /"classification":"unknown"/.test(x));
const eAdaugire = (x) => ADAUGIRI.test(x) || ADAUGIRE_TLS(x);
const adaugate = doarN0.filter(eAdaugire), doarN = doarN0.filter((x) => !eAdaugire(x));
if (adaugate.length) console.log(` (in afara echivalentei: ${adaugate.length} gasiri ale detectoarelor adaugate dupa 0.1.0, API-ul libsignal Java/Node si regulile JVM din 0.4.0)`);
const ok = !doarV.length && !doarN.length && ka.length + adaugate.length === kb.length && JSON.stringify(a.libraries) === JSON.stringify(b.libraries);
comparate += ka.length;

View File

@ -15,4 +15,17 @@ const a2 = tls.connect({ ecdhCurve: PQ }); // EXPECT: X25519MLKEM768 | quantum-s
const a3 = tls.connect({ ecdhCurve: `${PQ}:X25519` }); // EXPECT: X25519MLKEM768 | quantum-safe ; X25519 | quantum-vulnerable
const a4 = tls.connect({ ecdhCurve: GRUPURI[process.env.MOD] }); // EXPECT: tls ecdhCurve | unknown
module.exports = { server, agent, a2, a3, a4 };
// 0.4.1 (2026-10-19, D-540): versiunea data printr-o variabila. Pana la 0.4.0 niciuna din cele patru nu dadea vreo gasire TLS.
const MIN_TLS = 'TLSv1.2';
const a5 = tls.connect({ minVersion: MIN_TLS }); // EXPECT: TLSv1.2 | quantum-vulnerable
const modSus = process.env.MOD_SUS;
const minSus = modSus === 'hybrid-only' ? 'TLSv1.3' : 'TLSv1.2';
const a6 = tls.connect({ minVersion: minSus }); // EXPECT: TLSv1.2 | quantum-vulnerable
const a7 = tls.connect({ minVersion: process.env.TLS_MIN }); // EXPECT: tls minVersion | unknown
function a8(v) { return tls.connect({ minVersion: v }); } // EXPECT: tls minVersion | unknown
let minVar = modSus === 'x' ? 'TLSv1.3' : 'TLSv1.1';
minVar = 'TLSv1.3';
const a9 = tls.connect({ minVersion: minVar }); // EXPECT: tls minVersion | unknown
module.exports = { server, agent, a2, a3, a4, a5, a6, a7, a8, a9 };

View File

@ -19,7 +19,8 @@ const CA_JSON = process.argv.includes('--json');
// numarul total de marcaje EXPECT scrise de mana in fixturi; o schimbare aici se face constient
// (0.4.0, 2026-10-18: 158 + 111 din fixturile Swift si Kotlin, numarate de compara-fixturi inainte de a fi scrise aici)
const MARCAJE_ASTEPTATE = 269;
// 0.4.1 (2026-10-19, D-540): + 5 in js/tls.cjs (versiunea TLS printr-o constanta, un ternar, o expresie, un parametru, un ternar reasignat)
const MARCAJE_ASTEPTATE = 274;
const FISIERE_CU_MARCAJE = ['js/node-crypto.mjs', 'js/webcrypto.js', 'js/biblioteci.ts', 'js/tls.cjs', 'js/libsignal-client.ts', 'js/libsignal-client-import.ts', 'python/crypto_py.py', 'java/Crypto.java', 'java/Libsignal.java', 'java/LibsignalImport.java', 'go/crypto.go', 'rust/cripto.rs', 'rust/ambiguu.rs', 'rust/glob.rs', 'rust/libsignal.rs', 'negative/variabile.js',
'swift/libsignal.swift', 'swift/libsignal-import.swift', 'swift/cryptokit.swift', 'swift/securitate.swift', 'swift/biblioteci.swift', 'kotlin/Libsignal.kt', 'kotlin/Android.kt'];
// fixturile negative: fiecare fisier de aici e un fisier de cod citit si analizat, cu zero gasiri (in afara de variabile.js, care are marcaje)

View File

@ -2,7 +2,7 @@
A generic builder of AERE Proof Protocol envelopes (AIP-23) for the standard proof kinds: `data`, `execution`, `identity`,
`compliance`, `runtime`, `location`, `device`, `payment`, `ownership`, `time`, `block`, `authorization`, `settlement`,
`provenance` and, since 2026-10-18, `secret-access`. Every kind produces the same envelope (`statement` plus `statementHash`, the
`provenance`, since 2026-10-18 `secret-access` and since 2026-10-19 `agent-witness-checkpoint`. Every kind produces the same envelope (`statement` plus `statementHash`, the
SHA-256 of the canonical statement text), so the same AIP-23 reference verifier checks all of them with no kind-specific
verification code.
@ -25,15 +25,28 @@ review of the secrets service) it may also carry `requestHash` (the digest of th
`claimedHolder` / `claimedPresenter` (ids a presentation claimed and nobody verified) and `judged` (on a refusal, each grant judged
with the rules it refused on); envelopes without these fields are byte for byte what they were.
`agent-witness-checkpoint` is the checkpoint of the hosted agents witness (`agents/martor-agenti.mjs`), in the form of a transparency log
checkpoint (C2SP tlog-checkpoint: the log's name, its size, its RFC 9162 root): `root` and `previous` (the statementHash of the checkpoint
before it, absent on the first) are already 32-byte hashes and are refused in another form; `log`, `witness` (the id derived from the
witness's ML-DSA-65 key, which signs the envelope), `chainId` (where checkpoints are notarized), `treeSize` and `maxMergeDelay` (seconds
between receiving a head and covering it with a notarized checkpoint) have a vocabulary (below). The leaves of the tree are digests of the
signed ledger heads it received, nothing of any ledger.
**A kind may have a vocabulary** (`valori` in its schema: per field, a predicate and what it says), judged by `buildProof` on every
field present; `secret-access` is the only kind with one: `verdict` is `allowed`, `refused` or `failed`, `operation` one of the
field present; two kinds have one. `secret-access`: `verdict` is `allowed`, `refused` or `failed`, `operation` one of the
operations of the secrets service (`SECRET_ACCESS.operations`, which the service requires to be its own list), `version` an integer of
at least 1 or `null`, `rules` a list of rule names, ids `aere-id:` + 40 hex or `null`, `createdAt` an RFC 3339 time. An envelope with
the verdict `banana` is no longer built. The reference verifier does not judge vocabularies (it judges the form and the hash of any
kind), so an envelope written by hand with such values stays `VALID` there: the vocabulary is the builder's promise, not the
verifier's. `KINDS.json` of the published copy names the values of each field that has them.
verifier's. `agent-witness-checkpoint` (since 2026-10-19, the adversarial review of the hosted witness): `root` and `previous` 0x + 64
lowercase hex, `log` 1 to 256 printable ASCII characters without spaces, `witness` `aere-witness:` + 40 lowercase hex, `chainId`, `treeSize`
and `maxMergeDelay` integers of at least 1, `createdAt` an RFC 3339 time; the vocabulary is one exported object (`AGENT_WITNESS_CHECKPOINT`)
that the witness's own judge of a checkpoint (`verificaPunct` in `agents/martor-agenti.mjs`) imports, so the builder and the judge cannot
drift apart. Until then a checkpoint with `treeSize` "5" or `maxMergeDelay` 0 was built, and `VALID` at the reference verifier, while the
witness refused it. `KINDS.json` of the published copy names the values of each field that has them.
Test: `node proba-proof-kinds.mjs` (28 checks: every kind built and found `VALID` by the reference verifier, seven negative
Test: `node proba-proof-kinds.mjs` (32 checks since 2026-10-19, after the review of the hosted witness: every kind built and found `VALID` by the reference verifier, eight negative
controls such as content changed after hashing, a required field missing or a `secret-access` value outside its vocabulary, no raw
content in any envelope, bytes hashed as bytes, the new `secret-access` fields, the vocabulary held by that kind alone). It needs the AIP-23 reference verifier: `AERE_VERIFY_PROOF=<path to verify-proof.mjs>` (as published at
content in any envelope, bytes hashed as bytes, the new `secret-access` fields, the two vocabularies held by those two kinds alone, eleven values outside the vocabulary of the witness checkpoint
refused, and that vocabulary being the very object the witness imports). It needs the AIP-23 reference verifier: `AERE_VERIFY_PROOF=<path to verify-proof.mjs>` (as published at
aere.network/tools, with `verify-anchor.mjs`, `mpt.mjs`, `falcon512.mjs`, `registry-v3.mjs` and `package.json` beside it and `npm install` done there); without it the test reports that it did not measure and exits with 2. Node.js 24.

View File

@ -5,7 +5,7 @@
import fs from 'node:fs'; import crypto from 'node:crypto'; import os from 'node:os'; import path from 'node:path';
import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url';
import { buildProof, KINDS, SCHEME } from './proof-kinds.mjs';
import { buildProof, KINDS, SCHEME, AGENT_WITNESS_CHECKPOINT } from './proof-kinds.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const VERIFY = process.env.AERE_VERIFY_PROOF ? path.resolve(process.env.AERE_VERIFY_PROOF) : path.resolve(AICI, '..', 'aere-proof-protocol', 'verify.mjs');
@ -33,10 +33,12 @@ const INTRARI = {
settlement: { chainId: 2800, txHash: '0x' + '12'.repeat(32), blockHash: '0x' + '34'.repeat(32), from: '0xa', to: '0xb', amount: '100', asset: 'AERE', instruction: BRUT, createdAt: '2026-09-28T09:00:00Z' },
provenance: { subject: BRUT, parents: [BRUT + '1', BRUT + '2'], process: BRUT + 'p', actor: 'build-bot', createdAt: '2026-09-28T09:00:00Z' },
'secret-access': { service: 'https://secrets.example', secret: DB, version: 2, operation: 'get', verdict: 'allowed', rules: [], grant: 'deployers', holder: 'aere-id:' + '0'.repeat(40), presenter: 'aere-id:' + '1'.repeat(40), presentation: BRUT, policy: BRUT + 'pol', createdAt: '2026-10-18T09:00:00Z' },
// 2026-10-19 (B3): capul de jurnal al martorului gazduit al agentilor (radacina si capul de dinainte sunt deja hash-uri de 32 de octeti)
'agent-witness-checkpoint': { root: '0x' + '5a'.repeat(32), previous: '0x' + '6b'.repeat(32), log: 'aere-agent-witness/28001', witness: 'aere-witness:' + '7c'.repeat(20), chainId: 28001, treeSize: 3, maxMergeDelay: 1800, createdAt: '2026-10-19T09:00:00Z' },
};
try {
cer('schema acopera cele 15 feluri (10 + bloc + autorizare, decontare, provenienta + acces la secret)', KINDS.length >= 15 && ['authorization', 'settlement', 'provenance', 'secret-access'].every((k) => KINDS.includes(k)));
cer('schema acopera cele 16 feluri (10 + bloc + autorizare, decontare, provenienta + acces la secret + capul martorului agentilor)', KINDS.length >= 16 && ['authorization', 'settlement', 'provenance', 'secret-access', 'agent-witness-checkpoint'].every((k) => KINDS.includes(k)));
for (const k of KINDS) {
// fiecare fel se judeca separat: un fel care nu se mai poate construi e un RAU numit, nu o exceptie care opreste proba (2026-09-28)
let plic; try { plic = buildProof(k, INTRARI[k]); } catch (e) { cer(`${k}: constructia a cazut (${e.message})`, false); continue; }
@ -80,8 +82,22 @@ try {
const vechi = buildProof('secret-access', SA);
cer('acces la secret: requestHash, claimedPresenter, judged si verdictul failed se construiesc; fara ele declaratia are exact campurile de dinainte, in ordinea lor',
nou.statement.requestHash === '0x' + 'ab'.repeat(32) && nou.statement.verdict === 'failed' && Object.keys(vechi.statement).join(',') === 'v,kind,presentationHash,policyHash,service,secret,version,operation,verdict,rules,grant,holder,presenter,createdAt');
cer('vocabularul e numai al felului secret-access (celelalte feluri se construiesc exact ca ieri)', KINDS.filter((k) => SCHEME[k].valori).join(',') === 'secret-access');
cer('vocabularul e numai al felurilor secret-access si agent-witness-checkpoint (celelalte se construiesc exact ca ieri)', KINDS.filter((k) => SCHEME[k].valori).join(',') === 'secret-access,agent-witness-checkpoint');
// 2026-10-19 (B3): capul martorului agentilor: radacina trebuie sa fie deja un hash de 32 de octeti (nu se hashuieste), primul cap nu are
// `previous`, iar ordinea campurilor e cea a constructorului (semnatura martorului e peste exact acest text)
const AW = INTRARI['agent-witness-checkpoint'];
const primul = buildProof('agent-witness-checkpoint', { ...AW, previous: undefined });
cer('CONTROL: capul martorului cu radacina care nu e hash de 32 de octeti, sau fara maxMergeDelay -> eroare; primul cap fara previous; campurile in ordinea constructorului',
arunca(() => buildProof('agent-witness-checkpoint', { ...AW, root: 'radacina' })) && arunca(() => buildProof('agent-witness-checkpoint', { ...AW, maxMergeDelay: undefined }))
&& !('previous' in primul.statement) && Object.keys(buildProof('agent-witness-checkpoint', AW).statement).join(',') === 'v,kind,root,previous,log,witness,chainId,treeSize,maxMergeDelay,createdAt');
// 2026-10-19 (revizia adversariala B3, constatarea 8): VOCABULARUL capului martorului, scris o data (AGENT_WITNESS_CHECKPOINT) si importat si
// de judecatorul martorului (verificaPunct). Pana azi treeSize "5", maxMergeDelay 0 sau witness "x" se construiau si erau VALID la verificator.
const releAW = [['treeSize', '5'], ['treeSize', 0], ['chainId', '28001'], ['chainId', 0], ['maxMergeDelay', 0], ['maxMergeDelay', 1.5], ['witness', 'x'], ['witness', 'aere-witness:' + 'AB'.repeat(20)],
['log', ''], ['log', 'cu spatiu'], ['createdAt', 'ieri']].filter(([c, v]) => !arunca(() => buildProof('agent-witness-checkpoint', { ...AW, [c]: v })));
cer('CONTROL: capul martorului cu valori in afara vocabularului (treeSize "5" sau 0, chainId "28001" sau 0, maxMergeDelay 0 sau 1.5, witness "x" sau cu majuscule, log gol sau cu spatiu, createdAt "ieri") -> eroare la constructie' + (releAW.length ? ': construite ' + releAW.map(([c, v]) => c + '=' + JSON.stringify(v)).join(', ') : ''), releAW.length === 0);
cer('capul martorului: vocabularul e chiar obiectul exportat (AGENT_WITNESS_CHECKPOINT.valori), pe care il importa judecatorul martorului; valorile bune se construiesc octet cu octet ca ieri',
SCHEME['agent-witness-checkpoint'].valori === AGENT_WITNESS_CHECKPOINT.valori && Object.isFrozen(AGENT_WITNESS_CHECKPOINT.valori) && buildProof('agent-witness-checkpoint', AW).statementHash === '0x' + crypto.createHash('sha256').update(JSON.stringify({ v: 1, kind: 'aere-proof-of-agent-witness-checkpoint', root: AW.root, previous: AW.previous, log: AW.log, witness: AW.witness, chainId: AW.chainId, treeSize: AW.treeSize, maxMergeDelay: AW.maxMergeDelay, createdAt: AW.createdAt }), 'utf8').digest('hex'));
} finally { fs.rmSync(T, { recursive: true, force: true }); }
const total = KINDS.length + 13;
console.log(`\nProof of everything: ${total - rele}/${total} cum trebuia (${KINDS.length} feluri + 7 controale negative + acoperire + Buffer + parinti + campurile noi + vocabularul)`);
const total = KINDS.length + 16;
console.log(`\nProof of everything: ${total - rele}/${total} cum trebuia (${KINDS.length} feluri + 9 controale negative + acoperire + Buffer + parinti + campurile noi + doua vocabulare + capul martorului)`);
process.exit(rele ? 1 : 0);

View File

@ -29,6 +29,26 @@ const NUME_IN_SERVICIU = /^[a-z0-9][a-z0-9_-]{0,63}$/;
const eRegula = (r) => typeof r === 'string' && /^[a-z][a-z0-9-]{0,63}$/.test(r);
const eGrant = (g) => typeof g === 'string' && /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$/.test(g);
const eIdSauNull = (v) => v === null || (typeof v === 'string' && /^aere-id:[0-9a-f]{40}$/.test(v));
const eRfc3339 = (v) => typeof v === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/.test(v) && !Number.isNaN(Date.parse(v));
const eHash32 = (v) => typeof v === 'string' && /^0x[0-9a-f]{64}$/.test(v);
// 2026-10-19 (revizia adversariala B3, constatarea 8): VOCABULARUL capului de jurnal al martorului gazduit al agentilor, scris O SINGURA DATA
// aici. Pana azi constructorul felului copia campurile ca atare (treeSize '5', maxMergeDelay 0, witness 'x' se construiau, iar plicul era
// VALID la verificatorul AIP-23), in timp ce martorul (tools/agent-policy/martor-agenti.mjs, verificaPunct) judeca forma cu propria lui copie a
// regulilor. Acum verificaPunct importa exact acest obiect, deci cele doua nu mai pot diverge. Valorile bune nu se schimba: vectorii
// publicati raman octet cu octet aceiasi.
export const AGENT_WITNESS_CHECKPOINT = Object.freeze({
valori: Object.freeze({
root: [eHash32, 'a 32-byte hash, 0x + 64 lowercase hex (the RFC 9162 root of the log)'],
previous: [eHash32, 'the statementHash of the checkpoint before, 0x + 64 lowercase hex (absent on the first)'],
log: [(v) => typeof v === 'string' && v.length >= 1 && v.length <= 256 && /^[\x21-\x7e]+$/.test(v), '1 to 256 printable ASCII characters without spaces (the log name)'],
witness: [(v) => typeof v === 'string' && /^aere-witness:[0-9a-f]{40}$/.test(v), 'aere-witness: + 40 lowercase hex (derived from the ML-DSA-65 key of the witness)'],
chainId: [(v) => Number.isSafeInteger(v) && v >= 1, 'a positive integer (the chain the checkpoints are notarized on)'],
treeSize: [(v) => Number.isSafeInteger(v) && v >= 1, 'an integer >= 1 (the number of heads in the log)'],
maxMergeDelay: [(v) => Number.isSafeInteger(v) && v >= 1, 'an integer number of seconds >= 1 (the maximum merge delay)'],
createdAt: [eRfc3339, 'an RFC 3339 time'],
}),
});
// schema fiecarui fel: campuri de tip 'digest' (se hashuiesc), 'plain' (raman ca atare), 'bool', 'list'. Toate obligatorii daca in `req`.
export const SCHEME = {
@ -81,6 +101,15 @@ export const SCHEME = {
judged: [(v) => Array.isArray(v) && v.length <= 64 && v.every((x) => x && typeof x === 'object' && !Array.isArray(x) && Object.keys(x).sort().join(',') === 'grant,rules' && eGrant(x.grant) && Array.isArray(x.rules) && x.rules.length <= 64 && x.rules.every(eRegula)), 'a list of { grant, rules }, at most 64'],
createdAt: [(v) => typeof v === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/.test(v) && !Number.isNaN(Date.parse(v)), 'an RFC 3339 time'],
} },
// 2026-10-19 (roadmap pista B, B3, linia Agents): CAPUL DE JURNAL al martorului gazduit al agentilor (tools/agent-policy/martor-agenti.mjs),
// in forma unui checkpoint de jurnal transparent (C2SP tlog-checkpoint: numele jurnalului, marimea, radacina RFC 9162): marimea si radacina
// arborelui capetelor de registru primite, capul de dinainte (statementHash-ul lui), numele jurnalului, id-ul martorului (derivat din cheia
// lui ML-DSA-65, care semneaza plicul), lantul pe care se notarizeaza capetele (28001 in sandbox) si intarzierea maxima promisa intre primirea
// unui cap si acoperirea lui de un cap notarizat (secunde, ca MMD-ul din RFC 9162). Nimic din vreun registru: frunzele sunt digesturi.
// Felurile de dinainte nu se schimba cu nimic.
// 2026-10-19 (revizia B3): cu vocabularul de mai sus (AGENT_WITNESS_CHECKPOINT), importat si de judecatorul martorului.
'agent-witness-checkpoint': { hash: ['root', 'previous'], plain: { log: 'log', witness: 'witness', chainId: 'chainId', treeSize: 'treeSize', maxMergeDelay: 'maxMergeDelay' }, req: ['root', 'log', 'witness', 'chainId', 'treeSize', 'maxMergeDelay'],
valori: AGENT_WITNESS_CHECKPOINT.valori },
};
export const KINDS = Object.keys(SCHEME);