@aere/cloud 1.4.0: auditExport({ day, format }): the audit log of a day as a file (jsonl = the exact bytes whose sha256 you notarize, verified locally; cef for a SIEM)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
ade68667c0
commit
53fd47b23b
@ -49,6 +49,8 @@ const attested = await aere.readiness('example.com', { attest: true }); // + att
|
||||
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
|
||||
// the audit log of a day as a FILE (1.4.0): the exact bytes whose sha256 is the digest you notarize; format: 'cef' for a SIEM
|
||||
const file = await aere.auditExport({ day: '2026-09-17' }); // file.verified === true: sha256(file.bytes) is the stated digest
|
||||
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
|
||||
|
||||
13
index.mjs
13
index.mjs
@ -107,6 +107,19 @@ export class AereCloud {
|
||||
if (day) q.set('day', day);
|
||||
return this._req('GET', `/account/audit?${q}`);
|
||||
}
|
||||
// the same day as a file (1.4.0): format 'jsonl' = the EXACT bytes of the day file, whose sha256 is the digest you notarize
|
||||
// (`verified` says the bytes you received hash to the digest the gateway states); 'cef' = one ArcSight CEF line per request
|
||||
// for a SIEM, carrying the digest of the source file. Returns { day, format, bytes, text, entries, digest, verified }.
|
||||
async auditExport({ day, format = 'jsonl' } = {}) {
|
||||
const q = new URLSearchParams({ format }); if (day) q.set('day', day);
|
||||
const r = await this._req('GET', `/account/audit?${q}`, { raw: true });
|
||||
if (!r.ok) { let d = null; try { d = await r.json(); } catch { /* non-JSON */ } throw new AereCloudError(r.status, d, '/account/audit'); }
|
||||
const bytes = new Uint8Array(await r.arrayBuffer());
|
||||
const digest = r.headers.get('x-aere-digest');
|
||||
const local = '0x' + [...new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', bytes))].map((b) => b.toString(16).padStart(2, '0')).join('');
|
||||
return { day: r.headers.get('x-aere-day'), format, bytes, text: new TextDecoder().decode(bytes), entries: Number(r.headers.get('x-aere-entries') || 0), digest,
|
||||
verified: format === 'jsonl' ? local === digest : null };
|
||||
}
|
||||
|
||||
// ---- webhooks ----
|
||||
listWebhooks() { return this._req('GET', '/webhooks'); }
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@aere/cloud",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.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",
|
||||
|
||||
@ -113,6 +113,24 @@ await test('2026-09-18: readinessPerimeter(): ruta cu cheie, corpul poarta doar
|
||||
await assert.rejects(() => c409.readinessPerimeter('example.com', { attest: true }), (e) => e.status === 409);
|
||||
});
|
||||
|
||||
await test('2026-09-18: auditExport(): octetii zilei + digestul din antet; verified=true cand octetii dau digestul, FALSE cand nu il dau; cef nu pretinde verificare; un 400 ajunge la apelant', async () => {
|
||||
const { createHash } = await import('node:crypto');
|
||||
const corp = Buffer.from('{"t":"2026-09-17T10:00:00.000Z","m":"GET","p":"/v1/account","s":200}\n');
|
||||
const d = '0x' + createHash('sha256').update(corp).digest('hex');
|
||||
const raspuns = (octeti, digest, status = 200) => async (url, init) => new Response(octeti, { status, headers: { 'x-aere-digest': digest, 'x-aere-day': '2026-09-17', 'x-aere-entries': '1' } });
|
||||
const vazute = [];
|
||||
const c = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: async (u, i) => { vazute.push({ u, cheie: !!i.headers['x-api-key'] }); return raspuns(corp, d)(u, i); } });
|
||||
const e = await c.auditExport({ day: '2026-09-17' });
|
||||
assert.equal(e.verified, true); assert.equal(e.digest, d); assert.equal(e.entries, 1); assert.equal(e.text, corp.toString('utf8')); assert.equal(vazute[0].cheie, true);
|
||||
assert.ok(vazute[0].u.endsWith('/account/audit?format=jsonl&day=2026-09-17'), vazute[0].u);
|
||||
const stricat = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: raspuns(Buffer.from('alti octeti'), d) });
|
||||
assert.equal((await stricat.auditExport({ day: '2026-09-17' })).verified, false, 'octeti care nu dau digestul declarat nu au voie sa iasa verified');
|
||||
const cef = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: raspuns(Buffer.from('CEF:0|x\n'), d) });
|
||||
assert.equal((await cef.auditExport({ day: '2026-09-17', format: 'cef' })).verified, null, 'pentru cef digestul e al fisierului-sursa, nu al textului primit');
|
||||
const rau = new AereCloud({ apiKey: 'ak2800.0xAAA.AERE-SINTETIC-s', baseUrl: BAZA_TEST, fetch: async () => new Response(JSON.stringify({ error: 'bad_format' }), { status: 400 }) });
|
||||
await assert.rejects(() => rau.auditExport({ format: 'xml' }), (x) => x.status === 400);
|
||||
});
|
||||
|
||||
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 } });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user