diff --git a/README.md b/README.md index 755d8c6..6a23acf 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,10 @@ const full = await aere.proof('0x' + sha256Hex); // { block, txHash, finality // quantum readiness of a domain's public TLS edge (free, no key); attest:true notarizes the report digest on chain const scan = await aere.readiness('example.com'); // { score, verdict, summary, findings, … } const attested = await aere.readiness('example.com', { attest: true }); // + attestation: { reportHash, txHash, proof, … } +// the whole public perimeter of a domain (1.3.0): named hosts + common prefixes found through DNS, one report; keyed +const perimeter = await aere.readinessPerimeter('example.com', { hosts: ['api', 'mail'] }); +// perimeter.summary: { measured, withoutPqKeyExchange: [...], soonestCertificateExpiry, worstScore, verdict, complete } +// while summary.complete is false, call again: finished scans are cached; { attest: true } notarizes a complete report const fin = await aere.pqFinality('latest', 'testnet', 'both'); // AIP-21: { finality, agreement, postQuantumConfirmedByBoth, besu: {…}, nethermind: {…} } // pqVerify: pass interface:'external' to verify a standard FIPS 204/205 signature (the precompile implements the diff --git a/index.mjs b/index.mjs index f46284d..128bd64 100644 --- a/index.mjs +++ b/index.mjs @@ -86,6 +86,13 @@ export class AereCloud { return this._req('POST', '/pq/readiness', { body, auth: !!opts.attest }); } readinessReport(domain) { return this._req('GET', `/pq/readiness/${encodeURIComponent(domain)}`, { auth: false }); } + // the whole public perimeter of a domain in one report (keyed): the hosts you name (labels or names under the domain) plus common + // prefixes found through DNS, each measured with the same handshakes. The request has a deadline: while summary.complete is false, + // call again (finished scans are cached). { attest: true } notarizes the digest of a COMPLETE report on chain 2800. + readinessPerimeter(domain, opts = {}) { + const body = { domain, ...(opts.hosts ? { hosts: opts.hosts } : {}), ...(opts.discover === false ? { discover: false } : {}), ...(opts.attest ? { attest: true } : {}) }; + return this._req('POST', '/pq/readiness/perimeter', { body }); + } // AIP-21 (2026-09-18): the per-block post-quantum finality record (seals heard by a validator, re-verified, quorum // verdict). Free, no key. Testnet 28001 first; `network` is reserved for chain 2800 once the method reaches its validators. diff --git a/package.json b/package.json index 0fe451d..7b14832 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aere/cloud", - "version": "1.2.1", + "version": "1.3.0", "description": "Official client for the Aere Cloud API: keyed RPC, post-quantum verification, chain data, notarization, webhooks and gas sponsorship on Aere Network (chain 2800).", "type": "module", "main": "index.mjs", diff --git a/test/sdk.test.mjs b/test/sdk.test.mjs index 6f59b8c..13a78a9 100644 --- a/test/sdk.test.mjs +++ b/test/sdk.test.mjs @@ -100,6 +100,19 @@ await test('2026-09-17: proof(hash) si readiness(): ruta, cheia doar cand se cer const g = await c.readinessReport('aere.network'); assert.equal(g.cached, true); assert.equal(vazute[3].cheie, false); }); +await test('2026-09-18: readinessPerimeter(): ruta cu cheie, corpul poarta doar ce s-a cerut, discover:false se trimite, un 409 de raport incomplet ajunge la apelant', async () => { + const vazute = []; + const f = fetchFals({ 'POST /pq/readiness/perimeter': { status: 200, corp: { domain: 'example.com', summary: { measured: 3, complete: true } } } }); + const spion = async (url, init) => { vazute.push({ cheie: !!(init.headers && init.headers['x-api-key']), corp: init.body }); return f(url, init); }; + const c = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: spion }); + const r = await c.readinessPerimeter('example.com'); assert.equal(r.summary.measured, 3); assert.equal(vazute[0].cheie, true, 'perimetrul cere cheia'); assert.equal(vazute[0].corp, JSON.stringify({ domain: 'example.com' })); + await c.readinessPerimeter('example.com', { hosts: ['api', 'mail.example.com'], discover: false, attest: true }); + assert.deepEqual(JSON.parse(vazute[1].corp), { domain: 'example.com', hosts: ['api', 'mail.example.com'], discover: false, attest: true }); + await c.readinessPerimeter('example.com', { discover: true }); assert.equal(vazute[2].corp, JSON.stringify({ domain: 'example.com' }), 'discover:true e implicitul, nu se trimite'); + const c409 = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: fetchFals({ 'POST /pq/readiness/perimeter': { status: 409, corp: { attestationError: 'perimeter_incomplete' } } }) }); + await assert.rejects(() => c409.readinessPerimeter('example.com', { attest: true }), (e) => e.status === 409); +}); + await test('verifyWebhook: semnatura buna trece, una gresita pica, o litera in plus pica', async () => { const secret = 'AERE-SINTETIC-secretul-meu'; const corp = JSON.stringify({ event: 'pq-anchor', data: { height: 42 } });