@aarmos/avar-core 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +37 -74
- package/dist/index.js +137 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,4 +53,4 @@ Apache-2.0. See [`LICENSE`](./LICENSE) and [`NOTICE`](./NOTICE).
|
|
|
53
53
|
|
|
54
54
|
## Spec revision
|
|
55
55
|
|
|
56
|
-
Tracks `avar/1` spec revision `1.0
|
|
56
|
+
Tracks `avar/1` spec revision `1.1` (release candidate; backward-compatible with `1.0-rc1`). See the [AVAR spec](https://github.com/Aarmatix/avar-spec/blob/main/SPEC.md) for the current revision and changelog.
|
package/dist/index.d.ts
CHANGED
|
@@ -194,80 +194,43 @@ declare function signedBodyOf(entry: AvarEntry): Omit<AvarEntry, "signature" | "
|
|
|
194
194
|
|
|
195
195
|
declare function verifyBundle(bundle: AvarBundle): Promise<VerificationReport>;
|
|
196
196
|
|
|
197
|
-
type
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
};
|
|
226
|
-
type ReceiptDiff = {
|
|
227
|
-
equal: boolean;
|
|
228
|
-
kind: "bundle" | "entry";
|
|
229
|
-
specVersion: {
|
|
230
|
-
from: string;
|
|
231
|
-
to: string;
|
|
232
|
-
} | null;
|
|
233
|
-
chainHead: {
|
|
234
|
-
from: {
|
|
235
|
-
entryHash: string;
|
|
236
|
-
index: number;
|
|
237
|
-
};
|
|
238
|
-
to: {
|
|
239
|
-
entryHash: string;
|
|
240
|
-
index: number;
|
|
241
|
-
};
|
|
242
|
-
extended: boolean;
|
|
243
|
-
} | null;
|
|
244
|
-
devicePublicKeys: {
|
|
245
|
-
added: string[];
|
|
246
|
-
removed: string[];
|
|
247
|
-
};
|
|
248
|
-
entries: {
|
|
249
|
-
added: Array<{
|
|
250
|
-
id: string;
|
|
251
|
-
index: number;
|
|
252
|
-
}>;
|
|
253
|
-
removed: Array<{
|
|
254
|
-
id: string;
|
|
255
|
-
index: number;
|
|
256
|
-
}>;
|
|
257
|
-
modified: ReceiptEntryChange[];
|
|
258
|
-
unchanged: number;
|
|
259
|
-
};
|
|
260
|
-
/** Raw canonical diff of manifest + pubkeys (entries handled above). */
|
|
261
|
-
ops: DiffOp[];
|
|
262
|
-
};
|
|
197
|
+
type AarmosErrorCode = "BUNDLE_OVERSIZED" | "BUNDLE_NOT_ZIP" | "BUNDLE_MISSING_FILES" | "BUNDLE_INVALID_JSON" | "BUNDLE_INVALID_NDJSON" | "BUNDLE_SPEC_UNSUPPORTED" | "BUNDLE_EMPTY" | "SPEC_VERSION_MISMATCH" | "ENTRIES_SHA256_MISMATCH" | "SIGNATURE_MISMATCH" | "FINGERPRINT_MISMATCH" | "CHAIN_BROKEN" | "STEP_CHAIN_BROKEN" | "MANIFEST_INVALID" | "FILE_NOT_FOUND" | "FILE_UNREADABLE" | "UNKNOWN";
|
|
198
|
+
interface AarmosError {
|
|
199
|
+
code: AarmosErrorCode;
|
|
200
|
+
message: string;
|
|
201
|
+
hint: string;
|
|
202
|
+
docsUrl: string;
|
|
203
|
+
}
|
|
204
|
+
declare const AARMOS_ERROR_TEMPLATES: Record<AarmosErrorCode, Omit<AarmosError, "code">>;
|
|
205
|
+
declare function aarmosError(code: AarmosErrorCode, overrides?: Partial<Omit<AarmosError, "code">>): AarmosError;
|
|
206
|
+
/** One-line human-readable format used by the CLI on stderr / bridge diagnostics. */
|
|
207
|
+
declare function formatAarmosErrorLine(err: AarmosError): string;
|
|
208
|
+
/**
|
|
209
|
+
* Report-level classifier. Given an invalid VerificationReport, return the
|
|
210
|
+
* single most-important AarmosError to surface. Priority reflects severity:
|
|
211
|
+
* envelope > signature > fingerprint > chain > step-chain > format.
|
|
212
|
+
*/
|
|
213
|
+
declare function classifyReport(report: {
|
|
214
|
+
formatOk: boolean;
|
|
215
|
+
entriesSha256Ok: boolean;
|
|
216
|
+
signaturesOk: boolean;
|
|
217
|
+
fingerprintsOk: boolean;
|
|
218
|
+
chainOk: boolean;
|
|
219
|
+
perStepChainOk: boolean;
|
|
220
|
+
verdict: "valid" | "invalid" | "valid-with-warnings";
|
|
221
|
+
issues: {
|
|
222
|
+
kind: string;
|
|
223
|
+
detail?: string;
|
|
224
|
+
}[];
|
|
225
|
+
}): AarmosError | null;
|
|
263
226
|
/**
|
|
264
|
-
*
|
|
265
|
-
*
|
|
227
|
+
* Optional Error subclass so throw/catch sites can propagate an AarmosError
|
|
228
|
+
* through code that expects `Error`. Consumers can detect via
|
|
229
|
+
* `err instanceof AarmosErrorException` or `err && (err as any).aarmos`.
|
|
266
230
|
*/
|
|
267
|
-
declare
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
declare function diffToolManifests(a: unknown, b: unknown): CanonicalDiff;
|
|
231
|
+
declare class AarmosErrorException extends Error {
|
|
232
|
+
readonly aarmos: AarmosError;
|
|
233
|
+
constructor(err: AarmosError);
|
|
234
|
+
}
|
|
272
235
|
|
|
273
|
-
export { type
|
|
236
|
+
export { AARMOS_ERROR_TEMPLATES, type AarmosError, type AarmosErrorCode, AarmosErrorException, type AvarBundle, type AvarEntry, type BundleManifest, type BundlePubKeys, type DecisionStep, GENESIS_PREV_HASH, GENESIS_PREV_STEP_HASH, type PolicyHit, type TextStep, type ToolStep, type TraceStep, type VerificationIssue, type VerificationReport, aarmosError, canonicalize, classifyReport, computeDeviceFingerprint, computeEntryHash, computeStepHash, formatAarmosErrorLine, sha256Hex, signedBodyOf, utf8, verifyBundle, verifySignature };
|
package/dist/index.js
CHANGED
|
@@ -295,6 +295,138 @@ function computeChainHead(entries) {
|
|
|
295
295
|
return { entryHash: "", index: -1 };
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
// src/errors.ts
|
|
299
|
+
var SPEC_URL = "https://aarmos.io/docs/avar-spec";
|
|
300
|
+
var VERIFY_URL = "https://aarmos.io/trust/verify";
|
|
301
|
+
var QUICKSTART_URL = "https://aarmos.io/docs/quickstart";
|
|
302
|
+
var AARMOS_ERROR_TEMPLATES = {
|
|
303
|
+
BUNDLE_OVERSIZED: {
|
|
304
|
+
message: "That bundle is larger than the 25 MB browser limit.",
|
|
305
|
+
hint: "Run `aarmos verify <bundle>` locally instead \u2014 the CLI has no size cap.",
|
|
306
|
+
docsUrl: QUICKSTART_URL
|
|
307
|
+
},
|
|
308
|
+
BUNDLE_NOT_ZIP: {
|
|
309
|
+
message: "That file is not a valid .avar.zip.",
|
|
310
|
+
hint: "Bundles are zip archives. Rename or re-export the file, then try again.",
|
|
311
|
+
docsUrl: SPEC_URL
|
|
312
|
+
},
|
|
313
|
+
BUNDLE_MISSING_FILES: {
|
|
314
|
+
message: "Bundle is missing one or more required files.",
|
|
315
|
+
hint: "A valid AVAR bundle contains SPEC-VERSION, manifest.json, entries.ndjson, and pubkeys.json.",
|
|
316
|
+
docsUrl: SPEC_URL
|
|
317
|
+
},
|
|
318
|
+
BUNDLE_INVALID_JSON: {
|
|
319
|
+
message: "manifest.json or pubkeys.json is not valid JSON.",
|
|
320
|
+
hint: "The file exists but cannot be parsed. Re-export from the CLI to regenerate a clean bundle.",
|
|
321
|
+
docsUrl: SPEC_URL
|
|
322
|
+
},
|
|
323
|
+
BUNDLE_INVALID_NDJSON: {
|
|
324
|
+
message: "entries.ndjson contains a malformed line.",
|
|
325
|
+
hint: "Each line must be a self-contained JSON entry. One corrupt line usually means the file was edited by hand.",
|
|
326
|
+
docsUrl: SPEC_URL
|
|
327
|
+
},
|
|
328
|
+
BUNDLE_SPEC_UNSUPPORTED: {
|
|
329
|
+
message: "This bundle was produced by a spec version this verifier does not support.",
|
|
330
|
+
hint: "Update to the latest verifier (`npm i -g @aarmos/avar-core`) or re-export with a matching spec version.",
|
|
331
|
+
docsUrl: SPEC_URL
|
|
332
|
+
},
|
|
333
|
+
BUNDLE_EMPTY: {
|
|
334
|
+
message: "Bundle contains no entries.",
|
|
335
|
+
hint: "An empty ledger is unusual \u2014 check that the run actually produced receipts before exporting.",
|
|
336
|
+
docsUrl: SPEC_URL
|
|
337
|
+
},
|
|
338
|
+
SPEC_VERSION_MISMATCH: {
|
|
339
|
+
message: "This bundle uses a spec version this verifier does not recognize.",
|
|
340
|
+
hint: "Update the verifier (`npm i -g @aarmos/avar-core`) or re-export the bundle with matching spec version.",
|
|
341
|
+
docsUrl: SPEC_URL
|
|
342
|
+
},
|
|
343
|
+
ENTRIES_SHA256_MISMATCH: {
|
|
344
|
+
message: "entries.ndjson has been modified since the bundle was signed.",
|
|
345
|
+
hint: "The envelope hash in manifest.json no longer matches the file. Do not trust this bundle \u2014 re-export from the CLI.",
|
|
346
|
+
docsUrl: VERIFY_URL
|
|
347
|
+
},
|
|
348
|
+
SIGNATURE_MISMATCH: {
|
|
349
|
+
message: "One or more entry signatures do not match the embedded public keys.",
|
|
350
|
+
hint: "The bundle was modified after signing, or the wrong pubkeys.json is bundled.",
|
|
351
|
+
docsUrl: VERIFY_URL
|
|
352
|
+
},
|
|
353
|
+
FINGERPRINT_MISMATCH: {
|
|
354
|
+
message: "Device fingerprint on one or more entries does not match its public key.",
|
|
355
|
+
hint: "The entry claims a device whose public key does not hash to the declared fingerprint. Treat this bundle as untrusted.",
|
|
356
|
+
docsUrl: VERIFY_URL
|
|
357
|
+
},
|
|
358
|
+
CHAIN_BROKEN: {
|
|
359
|
+
message: "The per-entry hash chain is broken.",
|
|
360
|
+
hint: "An entry was inserted, removed, or edited after signing. The break index is shown in the issues list.",
|
|
361
|
+
docsUrl: VERIFY_URL
|
|
362
|
+
},
|
|
363
|
+
STEP_CHAIN_BROKEN: {
|
|
364
|
+
message: "The per-step hash chain inside one or more entries is broken.",
|
|
365
|
+
hint: "A decision or tool step was tampered with. The affected entry index is in the issues list.",
|
|
366
|
+
docsUrl: VERIFY_URL
|
|
367
|
+
},
|
|
368
|
+
MANIFEST_INVALID: {
|
|
369
|
+
message: "manifest.json is missing required fields or uses an unsupported format.",
|
|
370
|
+
hint: "Re-export the bundle from the CLI \u2014 do not edit manifest.json by hand.",
|
|
371
|
+
docsUrl: SPEC_URL
|
|
372
|
+
},
|
|
373
|
+
FILE_NOT_FOUND: {
|
|
374
|
+
message: "Receipt not found at the given path.",
|
|
375
|
+
hint: "Check the path \u2014 a common cause is running from a different working directory than expected.",
|
|
376
|
+
docsUrl: QUICKSTART_URL
|
|
377
|
+
},
|
|
378
|
+
FILE_UNREADABLE: {
|
|
379
|
+
message: "Receipt exists but could not be read.",
|
|
380
|
+
hint: "Check filesystem permissions on the file.",
|
|
381
|
+
docsUrl: QUICKSTART_URL
|
|
382
|
+
},
|
|
383
|
+
UNKNOWN: {
|
|
384
|
+
message: "Something went wrong while verifying that bundle.",
|
|
385
|
+
hint: "Try again with a fresh export. If the error persists, run `aarmos verify` locally to see the raw error.",
|
|
386
|
+
docsUrl: QUICKSTART_URL
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
function aarmosError(code, overrides = {}) {
|
|
390
|
+
const base = AARMOS_ERROR_TEMPLATES[code];
|
|
391
|
+
return {
|
|
392
|
+
code,
|
|
393
|
+
message: overrides.message ?? base.message,
|
|
394
|
+
hint: overrides.hint ?? base.hint,
|
|
395
|
+
docsUrl: overrides.docsUrl ?? base.docsUrl
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function formatAarmosErrorLine(err) {
|
|
399
|
+
return `\u2717 ${err.code}: ${err.message}
|
|
400
|
+
Next: ${err.hint}
|
|
401
|
+
Docs: ${err.docsUrl}`;
|
|
402
|
+
}
|
|
403
|
+
function classifyReport(report) {
|
|
404
|
+
if (report.verdict !== "invalid") return null;
|
|
405
|
+
if (!report.entriesSha256Ok) return aarmosError("ENTRIES_SHA256_MISMATCH");
|
|
406
|
+
if (!report.signaturesOk) return aarmosError("SIGNATURE_MISMATCH");
|
|
407
|
+
if (!report.fingerprintsOk) return aarmosError("FINGERPRINT_MISMATCH");
|
|
408
|
+
if (!report.chainOk) return aarmosError("CHAIN_BROKEN");
|
|
409
|
+
if (!report.perStepChainOk) return aarmosError("STEP_CHAIN_BROKEN");
|
|
410
|
+
if (!report.formatOk) {
|
|
411
|
+
const specIssue = report.issues.find((i) => i.kind === "spec-version-mismatch");
|
|
412
|
+
if (specIssue) {
|
|
413
|
+
return aarmosError("SPEC_VERSION_MISMATCH", {
|
|
414
|
+
message: specIssue.detail ? `Spec mismatch \u2014 ${specIssue.detail}` : void 0
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
return aarmosError("MANIFEST_INVALID");
|
|
418
|
+
}
|
|
419
|
+
return aarmosError("UNKNOWN");
|
|
420
|
+
}
|
|
421
|
+
var AarmosErrorException = class extends Error {
|
|
422
|
+
aarmos;
|
|
423
|
+
constructor(err) {
|
|
424
|
+
super(`${err.code}: ${err.message}`);
|
|
425
|
+
this.name = "AarmosErrorException";
|
|
426
|
+
this.aarmos = err;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
298
430
|
// src/diff.ts
|
|
299
431
|
function isPlainObject(v) {
|
|
300
432
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -454,9 +586,13 @@ function diffToolManifests(a, b) {
|
|
|
454
586
|
return diffCanonical(a, b);
|
|
455
587
|
}
|
|
456
588
|
export {
|
|
589
|
+
AARMOS_ERROR_TEMPLATES,
|
|
590
|
+
AarmosErrorException,
|
|
457
591
|
GENESIS_PREV_HASH,
|
|
458
592
|
GENESIS_PREV_STEP_HASH,
|
|
593
|
+
aarmosError,
|
|
459
594
|
canonicalize,
|
|
595
|
+
classifyReport,
|
|
460
596
|
computeDeviceFingerprint,
|
|
461
597
|
computeEntryHash,
|
|
462
598
|
computeStepHash,
|
|
@@ -464,6 +600,7 @@ export {
|
|
|
464
600
|
diffPolicies,
|
|
465
601
|
diffReceipts,
|
|
466
602
|
diffToolManifests,
|
|
603
|
+
formatAarmosErrorLine,
|
|
467
604
|
sha256Hex,
|
|
468
605
|
signedBodyOf,
|
|
469
606
|
utf8,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aarmos/avar-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Reference implementation of the AVAR (Aarmos Verifiable Action Record) spec — canonical JSON, Ed25519 signature verification, hash-chain math, and bundle verification. Pure, zero-dep, browser + Node.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|