@atrib/verify 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +177 -0
  3. package/dist/calculate.d.ts +32 -0
  4. package/dist/calculate.d.ts.map +1 -0
  5. package/dist/calculate.js +495 -0
  6. package/dist/calculate.js.map +1 -0
  7. package/dist/graph-fetch.d.ts +22 -0
  8. package/dist/graph-fetch.d.ts.map +1 -0
  9. package/dist/graph-fetch.js +60 -0
  10. package/dist/graph-fetch.js.map +1 -0
  11. package/dist/index.d.ts +15 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +24 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/policy-builder.d.ts +26 -0
  16. package/dist/policy-builder.d.ts.map +1 -0
  17. package/dist/policy-builder.js +49 -0
  18. package/dist/policy-builder.js.map +1 -0
  19. package/dist/recommendation.d.ts +29 -0
  20. package/dist/recommendation.d.ts.map +1 -0
  21. package/dist/recommendation.js +91 -0
  22. package/dist/recommendation.js.map +1 -0
  23. package/dist/resolve-identity.d.ts +84 -0
  24. package/dist/resolve-identity.d.ts.map +1 -0
  25. package/dist/resolve-identity.js +121 -0
  26. package/dist/resolve-identity.js.map +1 -0
  27. package/dist/revocations.d.ts +73 -0
  28. package/dist/revocations.d.ts.map +1 -0
  29. package/dist/revocations.js +83 -0
  30. package/dist/revocations.js.map +1 -0
  31. package/dist/types.d.ts +200 -0
  32. package/dist/types.d.ts.map +1 -0
  33. package/dist/types.js +25 -0
  34. package/dist/types.js.map +1 -0
  35. package/dist/verifier.d.ts +54 -0
  36. package/dist/verifier.d.ts.map +1 -0
  37. package/dist/verifier.js +213 -0
  38. package/dist/verifier.js.map +1 -0
  39. package/dist/verify-record.d.ts +149 -0
  40. package/dist/verify-record.d.ts.map +1 -0
  41. package/dist/verify-record.js +136 -0
  42. package/dist/verify-record.js.map +1 -0
  43. package/package.json +47 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-record.d.ts","sourceRoot":"","sources":["../src/verify-record.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,eAAe,EAMf,KAAK,WAAW,EACjB,MAAM,YAAY,CAAA;AAKnB;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC;;;OAGG;IACH,iBAAiB,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,EAAE,IAAI,GAAG,GAAG,GAAG,KAAK,GAAG,GAAG,GAAG,GAAG,CAAA;IACrD;;;OAGG;IACH,oBAAoB,EAAE,OAAO,CAAA;IAC7B;;;;OAIG;IACH,8BAA8B,EAAE,OAAO,CAAA;CACxC;AAUD,MAAM,WAAW,wBAAwB;IACvC,kDAAkD;IAClD,KAAK,EAAE,OAAO,CAAA;IACd,iEAAiE;IACjE,WAAW,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,oBAAoB,CAAA;IACjC;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,oBAAoB,CAAA;IAC7C;;;;OAIG;IACH,OAAO,EAAE,iBAAiB,CAAA;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,WAAW,CAAA;IAC/B;;;;;;;;OAQG;IACH,oBAAoB,CAAC,EAAE,WAAW,EAAE,CAAA;CACrC;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,wBAAwB,CAAC,CAyCnC;AAED,iBAAS,iBAAiB,CACxB,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,WAAW,GAAG,SAAS,GACzC,oBAAoB,CAetB;AAED,iBAAS,iBAAiB,CACxB,OAAO,EAAE,MAAM,EAAE,EACjB,UAAU,EAAE,WAAW,EAAE,EACzB,QAAQ,EAAE,MAAM,EAAE,GACjB,oBAAoB,CAuBtB;AAED,iBAAS,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAmBjF;AAID,eAAO,MAAM,aAAa;;;;;;;;CAQzB,CAAA"}
@@ -0,0 +1,136 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Per-record verification (single AtribRecord).
4
+ *
5
+ * Distinct from `AtribVerifier.verify(recommendationDoc)` which verifies a
6
+ * settlement recommendation document by re-running the §4.6 calculation.
7
+ * This module verifies one signed record at a time and surfaces the
8
+ * per-record annotations defined in the package README.
9
+ *
10
+ * Implemented annotations (this file):
11
+ * - provenance: { token, upstream_record_hash, upstream_resolved } (D044 / §1.2.6)
12
+ * - informed_by_resolution: { resolved: string[], dangling: string[] } (D041 / §1.2.5, §3.2.4)
13
+ * - posture: { timestamp_granularity, timestamp_consistent } (D045 / §8.4)
14
+ *
15
+ * Pending annotations (tracked in DECISIONS.md P005):
16
+ * - capability_check: D051 / §6.7 (needs @atrib/directory integration)
17
+ * - cross_attestation: D052 / §1.7.6 (needs `signers[]` type addition + transaction-record signing variant in @atrib/mcp)
18
+ * - cross_log_*: D050 / §2.11 (needs multi-log proof-bundle infrastructure)
19
+ * - tool_name_form / args_commitment_form: §8.2 / §8.3 (needs tool_name + args_hash fields on the record shape — currently only content_id is exposed)
20
+ */
21
+ import { base64urlDecode, base64urlEncode, canonicalRecord, hexEncode, sha256, verifyRecord as verifyRecordSignature, } from '@atrib/mcp';
22
+ const PROVENANCE_TOKEN_PATTERN = /^[A-Za-z0-9_-]{22}$/;
23
+ const SHA256_REF_PATTERN = /^sha256:[0-9a-f]{64}$/;
24
+ const GRANULARITY_MULTIPLIER = {
25
+ ms: 1,
26
+ s: 1000,
27
+ min: 60_000,
28
+ h: 3_600_000,
29
+ d: 86_400_000,
30
+ };
31
+ /**
32
+ * Verify one signed record.
33
+ *
34
+ * Always returns a result; never throws. Per spec §5.8 graceful-
35
+ * degradation: invalid inputs (malformed signature, malformed
36
+ * provenance_token, etc.) are surfaced via warnings + signatureOk=false
37
+ * rather than thrown errors.
38
+ */
39
+ export async function verifyRecord(record, options = {}) {
40
+ const warnings = [];
41
+ let signatureOk = false;
42
+ try {
43
+ signatureOk = await verifyRecordSignature(record);
44
+ if (!signatureOk)
45
+ warnings.push('signature verification failed');
46
+ }
47
+ catch (err) {
48
+ warnings.push(`signature verification error: ${err.message}`);
49
+ }
50
+ const result = {
51
+ valid: false,
52
+ signatureOk,
53
+ posture: detectPosture(record, warnings),
54
+ warnings,
55
+ };
56
+ // provenance_token is OPTIONAL per spec §1.2.6 — surface only when present.
57
+ const provenanceToken = record.provenance_token;
58
+ if (typeof provenanceToken === 'string') {
59
+ if (!PROVENANCE_TOKEN_PATTERN.test(provenanceToken)) {
60
+ warnings.push(`provenance_token has invalid format (expected 22-char base64url): ${provenanceToken.slice(0, 32)}`);
61
+ }
62
+ else {
63
+ result.provenance = resolveProvenance(provenanceToken, options.upstreamCandidate);
64
+ }
65
+ }
66
+ // informed_by is OPTIONAL per spec §1.2.5 — surface only when present and non-empty.
67
+ if (record.informed_by && record.informed_by.length > 0) {
68
+ result.informed_by_resolution = resolveInformedBy(record.informed_by, options.informedByCandidates ?? [], warnings);
69
+ }
70
+ result.valid = signatureOk && warnings.length === 0;
71
+ return result;
72
+ }
73
+ function resolveProvenance(token, upstreamCandidate) {
74
+ if (!upstreamCandidate) {
75
+ return { token, upstream_record_hash: null, upstream_resolved: false };
76
+ }
77
+ const candidateHashBytes = sha256(canonicalRecord(upstreamCandidate));
78
+ const candidateTokenBytes = candidateHashBytes.slice(0, 16);
79
+ const candidateToken = base64urlEncode(candidateTokenBytes);
80
+ if (candidateToken !== token) {
81
+ return { token, upstream_record_hash: null, upstream_resolved: false };
82
+ }
83
+ const fullHash = `sha256:${hexEncode(candidateHashBytes)}`;
84
+ return { token, upstream_record_hash: fullHash, upstream_resolved: true };
85
+ }
86
+ function resolveInformedBy(entries, candidates, warnings) {
87
+ // Pre-hash every candidate once. Each entry is a sha256:<64hex> ref;
88
+ // matching is by string comparison against the candidate's full hash.
89
+ const candidateHashes = new Set();
90
+ for (const c of candidates) {
91
+ candidateHashes.add(`sha256:${hexEncode(sha256(canonicalRecord(c)))}`);
92
+ }
93
+ const resolved = [];
94
+ const dangling = [];
95
+ for (const entry of entries) {
96
+ if (!SHA256_REF_PATTERN.test(entry)) {
97
+ warnings.push(`informed_by entry has invalid format: ${entry.slice(0, 80)}`);
98
+ continue;
99
+ }
100
+ if (candidateHashes.has(entry)) {
101
+ resolved.push(entry);
102
+ }
103
+ else {
104
+ dangling.push(entry);
105
+ }
106
+ }
107
+ return { resolved, dangling };
108
+ }
109
+ function detectPosture(record, warnings) {
110
+ const declared = record.timestamp_granularity;
111
+ const explicit = typeof declared === 'string';
112
+ const granularity = explicit ? declared : 'ms';
113
+ // Spec §8.4 invariant: timestamp must match the granularity's trailing-zero pattern.
114
+ const multiplier = GRANULARITY_MULTIPLIER[granularity];
115
+ const consistent = record.timestamp % multiplier === 0;
116
+ if (!consistent) {
117
+ warnings.push(`timestamp_granularity declares '${granularity}' but timestamp ${record.timestamp} is not a multiple of ${multiplier}`);
118
+ }
119
+ return {
120
+ timestamp_granularity: granularity,
121
+ timestamp_consistent: consistent,
122
+ timestamp_granularity_explicit: explicit,
123
+ };
124
+ }
125
+ // Re-exported defensively so consumers that want stricter validation can
126
+ // align with the same regex atrib-emit's input schema enforces.
127
+ export const __test_only__ = {
128
+ PROVENANCE_TOKEN_PATTERN,
129
+ SHA256_REF_PATTERN,
130
+ GRANULARITY_MULTIPLIER,
131
+ resolveProvenance,
132
+ resolveInformedBy,
133
+ detectPosture,
134
+ base64urlDecode,
135
+ };
136
+ //# sourceMappingURL=verify-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-record.js","sourceRoot":"","sources":["../src/verify-record.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@atrib/verify",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "description": "Merchant verification. Independently verify attribution settlement recommendations against the atrib spec.",
17
+ "author": "atrib <hello@atrib.dev>",
18
+ "license": "Apache-2.0",
19
+ "homepage": "https://atrib.dev",
20
+ "keywords": [
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "attribution",
24
+ "verification",
25
+ "settlement",
26
+ "merchant",
27
+ "ed25519"
28
+ ],
29
+ "dependencies": {
30
+ "@noble/ed25519": "^2.3.0",
31
+ "@noble/hashes": "^2.2.0",
32
+ "canonicalize": "^2.1.0",
33
+ "@atrib/mcp": "0.1.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^25.6.0",
37
+ "fast-check": "^4.7.0",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^3.2.4"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc",
43
+ "test": "vitest run",
44
+ "typecheck": "tsc --noEmit",
45
+ "clean": "rm -rf dist"
46
+ }
47
+ }