ai-trust/ links six existing proofs (agent identity, model artifact attestation, host runtime record, policy and signed ledger,
Proof of AI envelope, on-chain witness) into one bundle judged by one verifier that checks only the links; 13/13 in the lab,
16/16 live on testnet 28001 (ledger head notarized). proof-of-ai/ is the AIP-23 envelope builder it uses. verifiable-data/
publishes append-only versions with an RFC 9162 root and a sorted index per field, range and equality queries proven correct
and complete, selective disclosure per field, availability by sampling, and the AIP-23 data envelope notarized (version 2 on
testnet 28001). agents/agent-ancora.mjs gains notarizeEnvelope({kind}), the general form of notarizeHead. Each README says what
is not built: zero-knowledge queries need a prover and are not here. No third party has reviewed any of this.
62 lines
5.8 KiB
JavaScript
62 lines
5.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// vd.mjs: the publisher's command line of a verifiable dataset (roadmap point 20). Values on the command line are JSON ("x", 42).
|
|
// node vd.mjs creeaza <dir> --nume <name> [--index f1,f2] a new dataset folder
|
|
// node vd.mjs adauga <dir> <records.ndjson|records.json> append records ({id, ...fields}); never rewrites
|
|
// node vd.mjs angajeaza <dir> [--out c.json] a new version: commitment + its AIP-23 envelope (plicuri/<n>.json)
|
|
// node vd.mjs notarizeaza <dir> <n> --rpc URL --key-file f --ethers-dir d [--notary 0x..]
|
|
// the envelope's statementHash on AereNotary (refuses 2800 unless AERE_CONFIRM_MAINNET=yes)
|
|
// node vd.mjs interogheaza <dir> <n> --camp f (--eq J | [--de-la J] [--pana-la J]) [--campuri a,b|*] [--out answer.json]
|
|
// node vd.mjs inregistrare <dir> <n> <i> [--campuri a,b|*] [--out rec.json]
|
|
// node vd.mjs consistenta <dir> <a> <b> [--out proof.json]
|
|
// node vd.mjs auditeaza <dir> rebuild every version from the records and compare the roots
|
|
// node vd.mjs serveste <dir> [--port 8797] [--privat] the read-only HTTP face (serveste.mjs)
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { createRequire } from 'node:module';
|
|
import { creeaza, deschide, adauga, angajeaza, versiune, dovadaInregistrare, interogheaza, dovadaConsistenta, auditeaza, plicAngajament, hashAngajament } from './baza.mjs';
|
|
import { notarizeEnvelope } from '../agents/agent-ancora.mjs';
|
|
import { porneste } from './serveste.mjs';
|
|
|
|
const a = process.argv.slice(2); const cmd = a[0]; const dir = a[1];
|
|
const opt = (k, d = null) => { const i = a.indexOf(k); return i >= 0 && i + 1 < a.length ? a[i + 1] : d; };
|
|
const are = (k) => a.includes(k);
|
|
const J = (s) => (s === null ? undefined : JSON.parse(s));
|
|
const scrie = (o) => { const f = opt('--out'); const t = JSON.stringify(o, null, 1); if (f) { fs.writeFileSync(f, t); console.log('written ' + f); } else console.log(t); };
|
|
const plicuri = (d) => { const p = path.join(d, 'plicuri'); fs.mkdirSync(p, { recursive: true }); return p; };
|
|
|
|
try {
|
|
if (cmd === 'creeaza') { const db = creeaza(dir, { name: opt('--nume'), indexes: (opt('--index', '') || '').split(',').filter(Boolean) }); console.log('created ' + db.set.name + ' at ' + dir + ' (indexes: ' + (db.set.indexes.join(', ') || 'none') + ')'); }
|
|
else if (cmd === 'adauga') {
|
|
const db = deschide(dir); const t = fs.readFileSync(a[2], 'utf8');
|
|
const recs = a[2].endsWith('.ndjson') ? t.split('\n').filter(Boolean).map((l) => JSON.parse(l)) : JSON.parse(t);
|
|
console.log('appended ' + adauga(db, Array.isArray(recs) ? recs : [recs]) + ' records (' + db.inregistrari.length + ' in total)');
|
|
}
|
|
else if (cmd === 'angajeaza') {
|
|
const db = deschide(dir); const c = angajeaza(db); const env = plicAngajament(c);
|
|
fs.writeFileSync(path.join(plicuri(dir), c.version + '.json'), JSON.stringify(env, null, 1));
|
|
console.log(`version ${c.version}: ${c.size} records, log root ${c.logRoot.slice(0, 18)}.., commitment ${hashAngajament(c).slice(0, 18)}.., envelope ${env.statementHash.slice(0, 18)}..`);
|
|
scrie(c);
|
|
}
|
|
else if (cmd === 'notarizeaza') {
|
|
const db = deschide(dir); const c = versiune(db, a[2]); const f = path.join(dir, 'plicuri', c.version + '.json');
|
|
if (!fs.existsSync(f)) throw new Error('no envelope for version ' + c.version + ' (run angajeaza first)');
|
|
const env = JSON.parse(fs.readFileSync(f, 'utf8')); if (env.notarization) { console.log('already notarized: firstSeen ' + env.notarization.firstSeen); process.exitCode = 0; }
|
|
else {
|
|
const ethersDir = opt('--ethers-dir'); if (!ethersDir || !opt('--rpc') || !opt('--key-file')) throw new Error('--rpc, --key-file and --ethers-dir are required');
|
|
const { ethers } = createRequire(path.join(path.resolve(ethersDir), 'package.json'))('ethers');
|
|
const r = await notarizeEnvelope({ envelope: env, kind: 'aere-proof-of-data', rpc: opt('--rpc'), keyFile: opt('--key-file'), ethers, notary: opt('--notary') });
|
|
if (!r.ok) { console.log(r.motiv); process.exitCode = r.cod || 1; }
|
|
else { fs.writeFileSync(f, JSON.stringify(r.envelope, null, 1)); const n = r.envelope.notarization; console.log(`notarized on chain ${n.chainId}: firstSeen ${n.firstSeen}${n.txHash ? ', tx ' + n.txHash + ' block ' + n.block : ' (already)'}`); }
|
|
}
|
|
}
|
|
else if (cmd === 'interogheaza') {
|
|
const db = deschide(dir); const campuri = opt('--campuri', '');
|
|
scrie(interogheaza(db, a[2], { field: opt('--camp'), eq: J(opt('--eq')), from: J(opt('--de-la')), to: J(opt('--pana-la')), fields: campuri === '*' ? '*' : campuri.split(',').filter(Boolean), limit: Number(opt('--limita', 1000)) }));
|
|
}
|
|
else if (cmd === 'inregistrare') { const db = deschide(dir); const campuri = opt('--campuri', '*'); scrie(dovadaInregistrare(db, a[2], Number(a[3]), { fields: campuri === '*' ? '*' : campuri.split(',').filter(Boolean) })); }
|
|
else if (cmd === 'consistenta') { const db = deschide(dir); scrie(dovadaConsistenta(db, a[2], a[3])); }
|
|
else if (cmd === 'auditeaza') { const db = deschide(dir); const r = auditeaza(db); console.log(r.ok ? `AUDIT OK: ${db.versiuni.length} versions rebuilt from ${db.inregistrari.length} records, every root matches` : 'AUDIT FAILED:\n ' + r.probleme.join('\n ')); process.exitCode = r.ok ? 0 : 1; }
|
|
else if (cmd === 'serveste') { const s = await porneste({ dir, port: Number(opt('--port', 8797)), privat: are('--privat') }); console.log('serving ' + s.url + (are('--privat') ? ' (records without field values)' : '')); }
|
|
else { console.log('usage: vd.mjs creeaza|adauga|angajeaza|notarizeaza|interogheaza|inregistrare|consistenta|auditeaza|serveste ... (see the header)'); process.exitCode = 2; }
|
|
} catch (e) { console.log('ERROR: ' + String(e.message).slice(0, 300)); process.exitCode = 1; }
|