@mcp-audit-gateway/core 0.1.0 → 0.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.
Files changed (41) hide show
  1. package/.well-known/agent-governance.json +47 -0
  2. package/.well-known/security-insights-snippet.yml +9 -0
  3. package/CHANGELOG.md +32 -0
  4. package/dist/attestation/audit-log.d.ts +1 -0
  5. package/dist/attestation/audit-log.d.ts.map +1 -1
  6. package/dist/attestation/audit-log.js +19 -0
  7. package/dist/attestation/audit-log.js.map +1 -1
  8. package/dist/attestation/signer.d.ts.map +1 -1
  9. package/dist/attestation/signer.js +7 -0
  10. package/dist/attestation/signer.js.map +1 -1
  11. package/dist/attestation/signer.test.js +72 -0
  12. package/dist/attestation/signer.test.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/policy/engine.d.ts +12 -1
  18. package/dist/policy/engine.d.ts.map +1 -1
  19. package/dist/policy/engine.js +26 -4
  20. package/dist/policy/engine.js.map +1 -1
  21. package/dist/policy/engine.test.js +42 -1
  22. package/dist/policy/engine.test.js.map +1 -1
  23. package/dist/proxy/gateway.d.ts.map +1 -1
  24. package/dist/proxy/gateway.js +5 -1
  25. package/dist/proxy/gateway.js.map +1 -1
  26. package/dist/types.d.ts +7 -0
  27. package/dist/types.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/attestation/audit-log.ts +24 -1
  30. package/src/attestation/signer.test.ts +84 -0
  31. package/src/attestation/signer.ts +8 -1
  32. package/src/index.ts +1 -1
  33. package/src/policy/engine.test.ts +47 -1
  34. package/src/policy/engine.ts +38 -5
  35. package/src/proxy/gateway.ts +6 -1
  36. package/src/types.ts +8 -0
  37. package/test/vectors/README.md +44 -0
  38. package/test/vectors/canonicalization.json +582 -0
  39. package/test/vectors/generate.mjs +354 -0
  40. package/test/vectors/verify.mjs +273 -0
  41. package/test/vectors/verify.py +277 -0
@@ -0,0 +1,582 @@
1
+ {
2
+ "format_version": "1.1.0",
3
+ "implementation": "mcp-audit-gateway",
4
+ "canonical_form": "tuple-array",
5
+ "description": "Conformance vectors for mcp-audit-gateway's tuple-array canonicalization and SHA-256 hash chain.",
6
+ "encoding": "utf-8",
7
+ "hash_algorithm": "sha256",
8
+ "hash_output": "hex-lowercase",
9
+ "null_rule": "Fields listed in field_order that are absent or undefined in the source record MUST be serialized as JSON null in the tuple-array canonical form.",
10
+ "signing_exclusions": [
11
+ "attestation"
12
+ ],
13
+ "signing_note": "The attestation field is excluded from the canonical form used for signing. It cannot be included because the signature IS the attestation value (including it would be circular). The canonical form contains exactly the 11 fields listed in field_order.",
14
+ "chain_hash_note": "The chain hash (record_hash) uses JSON.stringify(fullRecord) which INCLUDES the attestation field. This binds the signature into the chain sequence, preventing attestation stripping without breaking the chain. The chain hash depends on JavaScript insertion order; verifiers must serialize keys in the order specified by chain.chain_key_order.",
15
+ "field_order": [
16
+ "id",
17
+ "timestamp",
18
+ "method",
19
+ "toolName",
20
+ "namespace",
21
+ "upstream",
22
+ "principal",
23
+ "durationMs",
24
+ "success",
25
+ "errorCode",
26
+ "previousHash"
27
+ ],
28
+ "conditional_fields": {
29
+ "decisionContextDigest": "Included at position 10 (before previousHash) when present and non-null.",
30
+ "parties": "Included after previousHash (or after decisionContextDigest if present) when present and non-null. The value is a JSON array of party objects serialized inline."
31
+ },
32
+ "canonicalization": [
33
+ {
34
+ "name": "genesis_all_fields",
35
+ "record": {
36
+ "id": "550e8400-e29b-41d4-a716-446655440001",
37
+ "timestamp": "2026-08-21T12:00:00.000Z",
38
+ "method": "tools/call",
39
+ "toolName": "weather_lookup",
40
+ "namespace": "external-apis",
41
+ "upstream": "weather-server",
42
+ "principal": "user:alice@example.com",
43
+ "durationMs": 142,
44
+ "success": true,
45
+ "previousHash": "genesis"
46
+ },
47
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440001\"],[\"timestamp\",\"2026-08-21T12:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"weather_lookup\"],[\"namespace\",\"external-apis\"],[\"upstream\",\"weather-server\"],[\"principal\",\"user:alice@example.com\"],[\"durationMs\",142],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
48
+ "sha256_canonical": "4e9c29d6e22fc2ef1f931994997b01f48f054c1294a7c287bfeece79417e5d43"
49
+ },
50
+ {
51
+ "name": "genesis_null_optionals",
52
+ "record": {
53
+ "id": "550e8400-e29b-41d4-a716-446655440002",
54
+ "timestamp": "2026-08-21T12:00:01.000Z",
55
+ "method": "tools/list",
56
+ "durationMs": 3,
57
+ "success": true,
58
+ "previousHash": "genesis"
59
+ },
60
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440002\"],[\"timestamp\",\"2026-08-21T12:00:01.000Z\"],[\"method\",\"tools/list\"],[\"toolName\",null],[\"namespace\",null],[\"upstream\",null],[\"principal\",null],[\"durationMs\",3],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
61
+ "sha256_canonical": "e6d9cb79804d69a479c59e2c4e162e90a505ee3cc311d68f6225332764b417c0"
62
+ },
63
+ {
64
+ "name": "error_with_code",
65
+ "record": {
66
+ "id": "550e8400-e29b-41d4-a716-446655440003",
67
+ "timestamp": "2026-08-21T12:00:02.500Z",
68
+ "method": "tools/call",
69
+ "toolName": "database_query",
70
+ "namespace": "internal",
71
+ "upstream": "db-server",
72
+ "principal": "service:backend-agent",
73
+ "durationMs": 5021,
74
+ "success": false,
75
+ "errorCode": -32603,
76
+ "previousHash": "genesis"
77
+ },
78
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440003\"],[\"timestamp\",\"2026-08-21T12:00:02.500Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"database_query\"],[\"namespace\",\"internal\"],[\"upstream\",\"db-server\"],[\"principal\",\"service:backend-agent\"],[\"durationMs\",5021],[\"success\",false],[\"errorCode\",-32603],[\"previousHash\",\"genesis\"]]",
79
+ "sha256_canonical": "01f4d49a71137289af02111ca6a6b2d7568a7ab4d28aefcfaa17a679bcda90b6"
80
+ },
81
+ {
82
+ "name": "zero_duration",
83
+ "record": {
84
+ "id": "550e8400-e29b-41d4-a716-446655440004",
85
+ "timestamp": "2026-08-21T12:00:03.000Z",
86
+ "method": "tools/call",
87
+ "toolName": "cache_get",
88
+ "namespace": "infra",
89
+ "upstream": "cache-server",
90
+ "durationMs": 0,
91
+ "success": true,
92
+ "previousHash": "genesis"
93
+ },
94
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440004\"],[\"timestamp\",\"2026-08-21T12:00:03.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"cache_get\"],[\"namespace\",\"infra\"],[\"upstream\",\"cache-server\"],[\"principal\",null],[\"durationMs\",0],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
95
+ "sha256_canonical": "e67ed42f822a66e7851de0044e7a02b9e66a525419c78166966fa88cca5c325e"
96
+ },
97
+ {
98
+ "name": "invalid_params_error",
99
+ "record": {
100
+ "id": "550e8400-e29b-41d4-a716-446655440005",
101
+ "timestamp": "2026-08-21T12:00:04.000Z",
102
+ "method": "tools/call",
103
+ "toolName": "send_email",
104
+ "namespace": "comms",
105
+ "upstream": "email-server",
106
+ "principal": "user:bob@example.com",
107
+ "durationMs": 12,
108
+ "success": false,
109
+ "errorCode": -32602,
110
+ "previousHash": "genesis"
111
+ },
112
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440005\"],[\"timestamp\",\"2026-08-21T12:00:04.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"send_email\"],[\"namespace\",\"comms\"],[\"upstream\",\"email-server\"],[\"principal\",\"user:bob@example.com\"],[\"durationMs\",12],[\"success\",false],[\"errorCode\",-32602],[\"previousHash\",\"genesis\"]]",
113
+ "sha256_canonical": "a67508503e33af3fcf179c34f3ff4e6a0c066870e481fb999f9d03e128f3af3b"
114
+ },
115
+ {
116
+ "name": "unicode_in_fields",
117
+ "record": {
118
+ "id": "550e8400-e29b-41d4-a716-446655440006",
119
+ "timestamp": "2026-08-21T12:00:05.000Z",
120
+ "method": "tools/call",
121
+ "toolName": "天気検索",
122
+ "namespace": "external-apis",
123
+ "upstream": "weather-server-⚡",
124
+ "principal": "user:tanaka.太郎@example.jp",
125
+ "durationMs": 88,
126
+ "success": true,
127
+ "previousHash": "genesis"
128
+ },
129
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440006\"],[\"timestamp\",\"2026-08-21T12:00:05.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"天気検索\"],[\"namespace\",\"external-apis\"],[\"upstream\",\"weather-server-⚡\"],[\"principal\",\"user:tanaka.太郎@example.jp\"],[\"durationMs\",88],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
130
+ "sha256_canonical": "715926536f94e0a05f59613c9b41a7be103df413202fb9bcdefee2a402697a4e"
131
+ },
132
+ {
133
+ "name": "max_safe_integer_duration",
134
+ "record": {
135
+ "id": "550e8400-e29b-41d4-a716-446655440007",
136
+ "timestamp": "2026-08-21T12:00:06.000Z",
137
+ "method": "tools/call",
138
+ "toolName": "long_running_task",
139
+ "namespace": "compute",
140
+ "upstream": "batch-server",
141
+ "principal": "service:scheduler",
142
+ "durationMs": 9007199254740991,
143
+ "success": true,
144
+ "previousHash": "genesis"
145
+ },
146
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440007\"],[\"timestamp\",\"2026-08-21T12:00:06.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"long_running_task\"],[\"namespace\",\"compute\"],[\"upstream\",\"batch-server\"],[\"principal\",\"service:scheduler\"],[\"durationMs\",9007199254740991],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
147
+ "sha256_canonical": "75c90c99a36a895d1ba5acdb05167443e0fd1a1b3bf2a305d4cefdcac024511a"
148
+ },
149
+ {
150
+ "name": "empty_string_tool_name",
151
+ "record": {
152
+ "id": "550e8400-e29b-41d4-a716-446655440008",
153
+ "timestamp": "2026-08-21T12:00:07.000Z",
154
+ "method": "tools/call",
155
+ "toolName": "",
156
+ "namespace": "",
157
+ "upstream": "legacy-server",
158
+ "principal": "user:admin",
159
+ "durationMs": 1,
160
+ "success": true,
161
+ "previousHash": "genesis"
162
+ },
163
+ "canonical": "[[\"id\",\"550e8400-e29b-41d4-a716-446655440008\"],[\"timestamp\",\"2026-08-21T12:00:07.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"\"],[\"namespace\",\"\"],[\"upstream\",\"legacy-server\"],[\"principal\",\"user:admin\"],[\"durationMs\",1],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
164
+ "sha256_canonical": "2343460398a9d803ccedd27d8cc1d9693897e58e0c4d8985e2e88ba60170835c"
165
+ }
166
+ ],
167
+ "chain": {
168
+ "description": "Three linked records demonstrating hash chain verification. The chain hash covers the FULL record including the attestation field. This means the attestation (signature) is bound into the chain sequence.",
169
+ "chain_algorithm": "sha256(JSON.stringify(fullRecord))",
170
+ "chain_key_order": [
171
+ "id",
172
+ "timestamp",
173
+ "method",
174
+ "toolName",
175
+ "namespace",
176
+ "upstream",
177
+ "principal",
178
+ "durationMs",
179
+ "success",
180
+ "previousHash",
181
+ "attestation"
182
+ ],
183
+ "chain_key_order_note": "The chain hash uses JSON.stringify which preserves JavaScript insertion order. Verifiers in languages without ordered maps MUST serialize keys in this exact order to reproduce chain hashes. The canonical form (tuple-array) does NOT have this limitation.",
184
+ "genesis_seed": "genesis",
185
+ "records": [
186
+ {
187
+ "record": {
188
+ "id": "660e8400-e29b-41d4-a716-446655440001",
189
+ "timestamp": "2026-08-21T13:00:00.000Z",
190
+ "method": "tools/call",
191
+ "toolName": "search",
192
+ "namespace": "research",
193
+ "upstream": "search-server",
194
+ "principal": "user:carol@example.com",
195
+ "durationMs": 230,
196
+ "success": true,
197
+ "previousHash": "genesis",
198
+ "attestation": "abc123deadbeef"
199
+ },
200
+ "full_record_json": "{\"id\":\"660e8400-e29b-41d4-a716-446655440001\",\"timestamp\":\"2026-08-21T13:00:00.000Z\",\"method\":\"tools/call\",\"toolName\":\"search\",\"namespace\":\"research\",\"upstream\":\"search-server\",\"principal\":\"user:carol@example.com\",\"durationMs\":230,\"success\":true,\"previousHash\":\"genesis\",\"attestation\":\"abc123deadbeef\"}",
201
+ "record_hash": "bcb759bae3e453c133cae1dd026b477522bc493084be15a84686d7b819eb2b99",
202
+ "canonical": "[[\"id\",\"660e8400-e29b-41d4-a716-446655440001\"],[\"timestamp\",\"2026-08-21T13:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"search\"],[\"namespace\",\"research\"],[\"upstream\",\"search-server\"],[\"principal\",\"user:carol@example.com\"],[\"durationMs\",230],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
203
+ "sha256_canonical": "2779a8c2eee23f409039929b49b7dd6df30e95ba9a3d83e6443d16c13ba5306a"
204
+ },
205
+ {
206
+ "record": {
207
+ "id": "660e8400-e29b-41d4-a716-446655440002",
208
+ "timestamp": "2026-08-21T13:00:01.500Z",
209
+ "method": "tools/call",
210
+ "toolName": "summarize",
211
+ "namespace": "research",
212
+ "upstream": "llm-server",
213
+ "principal": "user:carol@example.com",
214
+ "durationMs": 1840,
215
+ "success": true,
216
+ "previousHash": "bcb759bae3e453c133cae1dd026b477522bc493084be15a84686d7b819eb2b99",
217
+ "attestation": "def456cafebabe"
218
+ },
219
+ "full_record_json": "{\"id\":\"660e8400-e29b-41d4-a716-446655440002\",\"timestamp\":\"2026-08-21T13:00:01.500Z\",\"method\":\"tools/call\",\"toolName\":\"summarize\",\"namespace\":\"research\",\"upstream\":\"llm-server\",\"principal\":\"user:carol@example.com\",\"durationMs\":1840,\"success\":true,\"previousHash\":\"bcb759bae3e453c133cae1dd026b477522bc493084be15a84686d7b819eb2b99\",\"attestation\":\"def456cafebabe\"}",
220
+ "record_hash": "8890ec9936274006781013783a3e4b319ae4e912bb14a597800efc9347a29abe",
221
+ "canonical": "[[\"id\",\"660e8400-e29b-41d4-a716-446655440002\"],[\"timestamp\",\"2026-08-21T13:00:01.500Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"summarize\"],[\"namespace\",\"research\"],[\"upstream\",\"llm-server\"],[\"principal\",\"user:carol@example.com\"],[\"durationMs\",1840],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"bcb759bae3e453c133cae1dd026b477522bc493084be15a84686d7b819eb2b99\"]]",
222
+ "sha256_canonical": "89bf2344f791f6f20bbb74fc8dfffeca1c1a3877cf60fa225c5d7292e8189bb9",
223
+ "previous_record_hash": "bcb759bae3e453c133cae1dd026b477522bc493084be15a84686d7b819eb2b99"
224
+ },
225
+ {
226
+ "record": {
227
+ "id": "660e8400-e29b-41d4-a716-446655440003",
228
+ "timestamp": "2026-08-21T13:00:05.000Z",
229
+ "method": "tools/call",
230
+ "toolName": "store_result",
231
+ "namespace": "research",
232
+ "upstream": "storage-server",
233
+ "principal": "user:carol@example.com",
234
+ "durationMs": 45,
235
+ "success": true,
236
+ "previousHash": "8890ec9936274006781013783a3e4b319ae4e912bb14a597800efc9347a29abe",
237
+ "attestation": "789abcfeedface"
238
+ },
239
+ "full_record_json": "{\"id\":\"660e8400-e29b-41d4-a716-446655440003\",\"timestamp\":\"2026-08-21T13:00:05.000Z\",\"method\":\"tools/call\",\"toolName\":\"store_result\",\"namespace\":\"research\",\"upstream\":\"storage-server\",\"principal\":\"user:carol@example.com\",\"durationMs\":45,\"success\":true,\"previousHash\":\"8890ec9936274006781013783a3e4b319ae4e912bb14a597800efc9347a29abe\",\"attestation\":\"789abcfeedface\"}",
240
+ "record_hash": "287b9307278203eab4429fea6636492044f428b96ade97910aa7ad86303ffc16",
241
+ "canonical": "[[\"id\",\"660e8400-e29b-41d4-a716-446655440003\"],[\"timestamp\",\"2026-08-21T13:00:05.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"store_result\"],[\"namespace\",\"research\"],[\"upstream\",\"storage-server\"],[\"principal\",\"user:carol@example.com\"],[\"durationMs\",45],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"8890ec9936274006781013783a3e4b319ae4e912bb14a597800efc9347a29abe\"]]",
242
+ "sha256_canonical": "d049da892b814c95c3e5c31e4756c5db26023a85e6f41e2c38984146b3fb2597",
243
+ "previous_record_hash": "8890ec9936274006781013783a3e4b319ae4e912bb14a597800efc9347a29abe"
244
+ }
245
+ ]
246
+ },
247
+ "dual_hash_demo": {
248
+ "description": "Two records with identical auditable fields but different attestation values. They produce the SAME canonical hash (attestation is excluded from signing) but DIFFERENT chain hashes (attestation is included in chain). This demonstrates the dual-hash design.",
249
+ "record_a": {
250
+ "record": {
251
+ "id": "770e8400-e29b-41d4-a716-446655440001",
252
+ "timestamp": "2026-08-21T14:00:00.000Z",
253
+ "method": "tools/call",
254
+ "toolName": "verify_identity",
255
+ "namespace": "auth",
256
+ "upstream": "identity-server",
257
+ "principal": "user:dave@example.com",
258
+ "durationMs": 55,
259
+ "success": true,
260
+ "previousHash": "genesis",
261
+ "attestation": "sig_aaa111"
262
+ },
263
+ "canonical": "[[\"id\",\"770e8400-e29b-41d4-a716-446655440001\"],[\"timestamp\",\"2026-08-21T14:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"verify_identity\"],[\"namespace\",\"auth\"],[\"upstream\",\"identity-server\"],[\"principal\",\"user:dave@example.com\"],[\"durationMs\",55],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
264
+ "sha256_canonical": "0443b29e0a27962c296cd4e854f954491213515426eb0d25adc773c463e02e00",
265
+ "full_record_json": "{\"id\":\"770e8400-e29b-41d4-a716-446655440001\",\"timestamp\":\"2026-08-21T14:00:00.000Z\",\"method\":\"tools/call\",\"toolName\":\"verify_identity\",\"namespace\":\"auth\",\"upstream\":\"identity-server\",\"principal\":\"user:dave@example.com\",\"durationMs\":55,\"success\":true,\"previousHash\":\"genesis\",\"attestation\":\"sig_aaa111\"}",
266
+ "record_hash": "16943a93113fe9895979585a6a972a12eca7d94a67370a8abd8eefa52deafdac"
267
+ },
268
+ "record_b": {
269
+ "record": {
270
+ "id": "770e8400-e29b-41d4-a716-446655440001",
271
+ "timestamp": "2026-08-21T14:00:00.000Z",
272
+ "method": "tools/call",
273
+ "toolName": "verify_identity",
274
+ "namespace": "auth",
275
+ "upstream": "identity-server",
276
+ "principal": "user:dave@example.com",
277
+ "durationMs": 55,
278
+ "success": true,
279
+ "previousHash": "genesis",
280
+ "attestation": "sig_bbb222"
281
+ },
282
+ "canonical": "[[\"id\",\"770e8400-e29b-41d4-a716-446655440001\"],[\"timestamp\",\"2026-08-21T14:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"verify_identity\"],[\"namespace\",\"auth\"],[\"upstream\",\"identity-server\"],[\"principal\",\"user:dave@example.com\"],[\"durationMs\",55],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
283
+ "sha256_canonical": "0443b29e0a27962c296cd4e854f954491213515426eb0d25adc773c463e02e00",
284
+ "full_record_json": "{\"id\":\"770e8400-e29b-41d4-a716-446655440001\",\"timestamp\":\"2026-08-21T14:00:00.000Z\",\"method\":\"tools/call\",\"toolName\":\"verify_identity\",\"namespace\":\"auth\",\"upstream\":\"identity-server\",\"principal\":\"user:dave@example.com\",\"durationMs\":55,\"success\":true,\"previousHash\":\"genesis\",\"attestation\":\"sig_bbb222\"}",
285
+ "record_hash": "770efbb50562d529f271f94b26f373749ddfda9936f4305a5ec2c4a256bf24b7"
286
+ },
287
+ "assertions": {
288
+ "canonical_hashes_match": true,
289
+ "chain_hashes_differ": true
290
+ }
291
+ },
292
+ "party_attribution": {
293
+ "description": "Vectors demonstrating multi-party attribution in the canonical form. The parties field is an array of {party, role, scope} objects that declares which entity asserted which fields. It appears at the end of the tuple-array (after previousHash, or after decisionContextDigest if present). Records without parties omit the field entirely from the canonical form. Includes edge cases: empty array vs null, scope ordering significance, and chain continuity with parties.",
294
+ "vectors": [
295
+ {
296
+ "name": "parties_gateway_only",
297
+ "description": "Single-party record: gateway witnesses all 11 base fields.",
298
+ "record": {
299
+ "id": "880e8400-e29b-41d4-a716-446655440001",
300
+ "timestamp": "2026-08-22T10:00:00.000Z",
301
+ "method": "tools/call",
302
+ "toolName": "read_file",
303
+ "namespace": "filesystem",
304
+ "upstream": "fs-server",
305
+ "principal": "user:eve@example.com",
306
+ "durationMs": 34,
307
+ "success": true,
308
+ "previousHash": "genesis",
309
+ "parties": [
310
+ {
311
+ "party": "gateway",
312
+ "role": "witness",
313
+ "scope": [
314
+ "id",
315
+ "timestamp",
316
+ "method",
317
+ "toolName",
318
+ "namespace",
319
+ "upstream",
320
+ "principal",
321
+ "durationMs",
322
+ "success",
323
+ "errorCode",
324
+ "previousHash"
325
+ ]
326
+ }
327
+ ]
328
+ },
329
+ "canonical": "[[\"id\",\"880e8400-e29b-41d4-a716-446655440001\"],[\"timestamp\",\"2026-08-22T10:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"read_file\"],[\"namespace\",\"filesystem\"],[\"upstream\",\"fs-server\"],[\"principal\",\"user:eve@example.com\"],[\"durationMs\",34],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"],[\"parties\",[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"id\",\"timestamp\",\"method\",\"toolName\",\"namespace\",\"upstream\",\"principal\",\"durationMs\",\"success\",\"errorCode\",\"previousHash\"]}]]]",
330
+ "sha256_canonical": "57cecd4f1d3f7f3f745a1e8cf1188f9a2d019cfe992edfebb506c4f3cd000231"
331
+ },
332
+ {
333
+ "name": "parties_dual_attribution",
334
+ "description": "Dual-party record: gateway witnesses base fields, policy-engine asserts decisionContextDigest. Demonstrates the 11:1 field split.",
335
+ "record": {
336
+ "id": "880e8400-e29b-41d4-a716-446655440002",
337
+ "timestamp": "2026-08-22T10:00:01.000Z",
338
+ "method": "tools/call",
339
+ "toolName": "execute_query",
340
+ "namespace": "database",
341
+ "upstream": "db-server",
342
+ "principal": "service:data-agent",
343
+ "durationMs": 412,
344
+ "success": true,
345
+ "decisionContextDigest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
346
+ "previousHash": "genesis",
347
+ "parties": [
348
+ {
349
+ "party": "gateway",
350
+ "role": "witness",
351
+ "scope": [
352
+ "id",
353
+ "timestamp",
354
+ "method",
355
+ "toolName",
356
+ "namespace",
357
+ "upstream",
358
+ "principal",
359
+ "durationMs",
360
+ "success",
361
+ "errorCode",
362
+ "previousHash"
363
+ ]
364
+ },
365
+ {
366
+ "party": "policy-engine",
367
+ "role": "asserter",
368
+ "scope": [
369
+ "decisionContextDigest"
370
+ ]
371
+ }
372
+ ]
373
+ },
374
+ "canonical": "[[\"id\",\"880e8400-e29b-41d4-a716-446655440002\"],[\"timestamp\",\"2026-08-22T10:00:01.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"execute_query\"],[\"namespace\",\"database\"],[\"upstream\",\"db-server\"],[\"principal\",\"service:data-agent\"],[\"durationMs\",412],[\"success\",true],[\"errorCode\",null],[\"decisionContextDigest\",\"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\"],[\"previousHash\",\"genesis\"],[\"parties\",[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"id\",\"timestamp\",\"method\",\"toolName\",\"namespace\",\"upstream\",\"principal\",\"durationMs\",\"success\",\"errorCode\",\"previousHash\"]},{\"party\":\"policy-engine\",\"role\":\"asserter\",\"scope\":[\"decisionContextDigest\"]}]]]",
375
+ "sha256_canonical": "bd81a3ab5c5e0b8bc910348ddbd77d274a2f3664260c96023df0a6e4ac2bc741"
376
+ },
377
+ {
378
+ "name": "no_parties_backward_compat",
379
+ "description": "Record without parties field — canonical form identical to pre-parties vectors. Proves backward compatibility.",
380
+ "record": {
381
+ "id": "880e8400-e29b-41d4-a716-446655440003",
382
+ "timestamp": "2026-08-22T10:00:02.000Z",
383
+ "method": "tools/list",
384
+ "durationMs": 2,
385
+ "success": true,
386
+ "previousHash": "genesis"
387
+ },
388
+ "canonical": "[[\"id\",\"880e8400-e29b-41d4-a716-446655440003\"],[\"timestamp\",\"2026-08-22T10:00:02.000Z\"],[\"method\",\"tools/list\"],[\"toolName\",null],[\"namespace\",null],[\"upstream\",null],[\"principal\",null],[\"durationMs\",2],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"genesis\"]]",
389
+ "sha256_canonical": "e6468120453a560a5d55c6b37fdc0df35213f9ffa3f93042d1583341ab530621"
390
+ },
391
+ {
392
+ "name": "parties_empty_array",
393
+ "description": "An empty parties array ([]) is NOT null and MUST be included in the canonical form. Distinguishes \"no attribution declared\" (null/absent = excluded) from \"parties explicitly empty\" ([] = included).",
394
+ "record": {
395
+ "id": "rec_empty_parties_001",
396
+ "timestamp": "2026-08-22T10:00:00.000Z",
397
+ "method": "tools/call",
398
+ "toolName": "validate",
399
+ "inputHash": "sha256:abc123",
400
+ "outputHash": "sha256:def456",
401
+ "durationMs": 42,
402
+ "success": true,
403
+ "errorCode": null,
404
+ "previousHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
405
+ "namespace": "test",
406
+ "upstream": null,
407
+ "principal": "user:tester",
408
+ "parties": []
409
+ },
410
+ "canonical": "[[\"id\",\"rec_empty_parties_001\"],[\"timestamp\",\"2026-08-22T10:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"validate\"],[\"namespace\",\"test\"],[\"upstream\",null],[\"principal\",\"user:tester\"],[\"durationMs\",42],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"sha256:0000000000000000000000000000000000000000000000000000000000000000\"],[\"parties\",[]]]",
411
+ "sha256_canonical": "14b5da4505f02cd8414f24556e19b787fa7d9fc0fb033778f9ed512f9a265a16"
412
+ },
413
+ {
414
+ "name": "scope_order_original",
415
+ "description": "Scope array order is significant. [\"toolName\", \"id\", \"timestamp\"] produces a DIFFERENT hash than alphabetically sorted. Implementations MUST NOT sort scope arrays.",
416
+ "record": {
417
+ "id": "rec_scope_order_001",
418
+ "timestamp": "2026-08-22T11:00:00.000Z",
419
+ "method": "tools/call",
420
+ "toolName": "execute",
421
+ "inputHash": "sha256:aaa111",
422
+ "outputHash": "sha256:bbb222",
423
+ "durationMs": 100,
424
+ "success": true,
425
+ "errorCode": null,
426
+ "previousHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
427
+ "namespace": "ordering-test",
428
+ "upstream": null,
429
+ "principal": "user:verifier",
430
+ "parties": [
431
+ {
432
+ "party": "gateway",
433
+ "role": "witness",
434
+ "scope": [
435
+ "toolName",
436
+ "id",
437
+ "timestamp"
438
+ ]
439
+ }
440
+ ]
441
+ },
442
+ "canonical": "[[\"id\",\"rec_scope_order_001\"],[\"timestamp\",\"2026-08-22T11:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"execute\"],[\"namespace\",\"ordering-test\"],[\"upstream\",null],[\"principal\",\"user:verifier\"],[\"durationMs\",100],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"sha256:1111111111111111111111111111111111111111111111111111111111111111\"],[\"parties\",[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"toolName\",\"id\",\"timestamp\"]}]]]",
443
+ "sha256_canonical": "ae6803d101ac18ec66d883c15ca20b3ace811fc1f1d77938b653ff7e5066b3e0"
444
+ },
445
+ {
446
+ "name": "scope_order_sorted",
447
+ "description": "Same record as scope_order_original but with scope alphabetically sorted. Hash MUST differ, proving scope order is normative.",
448
+ "record": {
449
+ "id": "rec_scope_order_001",
450
+ "timestamp": "2026-08-22T11:00:00.000Z",
451
+ "method": "tools/call",
452
+ "toolName": "execute",
453
+ "inputHash": "sha256:aaa111",
454
+ "outputHash": "sha256:bbb222",
455
+ "durationMs": 100,
456
+ "success": true,
457
+ "errorCode": null,
458
+ "previousHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
459
+ "namespace": "ordering-test",
460
+ "upstream": null,
461
+ "principal": "user:verifier",
462
+ "parties": [
463
+ {
464
+ "party": "gateway",
465
+ "role": "witness",
466
+ "scope": [
467
+ "id",
468
+ "timestamp",
469
+ "toolName"
470
+ ]
471
+ }
472
+ ]
473
+ },
474
+ "canonical": "[[\"id\",\"rec_scope_order_001\"],[\"timestamp\",\"2026-08-22T11:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"execute\"],[\"namespace\",\"ordering-test\"],[\"upstream\",null],[\"principal\",\"user:verifier\"],[\"durationMs\",100],[\"success\",true],[\"errorCode\",null],[\"previousHash\",\"sha256:1111111111111111111111111111111111111111111111111111111111111111\"],[\"parties\",[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"id\",\"timestamp\",\"toolName\"]}]]]",
475
+ "sha256_canonical": "ab3287cabbfc2970ae5c1111a8fb32c9ab5b8d0382488a693229a68527ff8289"
476
+ }
477
+ ],
478
+ "chain_with_parties": {
479
+ "description": "Two-record chain where both records carry party attribution. Proves parties are included in record_hash used for chain linkage.",
480
+ "genesis_seed": "sha256:chain_parties_genesis_0000000000000000000000000000000000000000",
481
+ "records": [
482
+ {
483
+ "record": {
484
+ "id": "rec_chain_pa_001",
485
+ "timestamp": "2026-08-22T12:00:00.000Z",
486
+ "method": "tools/call",
487
+ "toolName": "fetch_data",
488
+ "inputHash": "sha256:input_fetch",
489
+ "outputHash": "sha256:output_fetch",
490
+ "durationMs": 230,
491
+ "success": true,
492
+ "errorCode": null,
493
+ "previousHash": "sha256:chain_parties_genesis_0000000000000000000000000000000000000000",
494
+ "namespace": "chain-party-test",
495
+ "upstream": "https://api.example.com",
496
+ "principal": "user:chain-tester",
497
+ "decisionContextDigest": "sha256:policy_eval_001",
498
+ "parties": [
499
+ {
500
+ "party": "gateway",
501
+ "role": "witness",
502
+ "scope": [
503
+ "id",
504
+ "timestamp",
505
+ "method",
506
+ "toolName",
507
+ "namespace",
508
+ "upstream",
509
+ "principal",
510
+ "durationMs",
511
+ "success",
512
+ "errorCode",
513
+ "previousHash"
514
+ ]
515
+ },
516
+ {
517
+ "party": "policy-engine",
518
+ "role": "asserter",
519
+ "scope": [
520
+ "decisionContextDigest"
521
+ ]
522
+ }
523
+ ]
524
+ },
525
+ "canonical": "[[\"id\",\"rec_chain_pa_001\"],[\"timestamp\",\"2026-08-22T12:00:00.000Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"fetch_data\"],[\"namespace\",\"chain-party-test\"],[\"upstream\",\"https://api.example.com\"],[\"principal\",\"user:chain-tester\"],[\"durationMs\",230],[\"success\",true],[\"errorCode\",null],[\"decisionContextDigest\",\"sha256:policy_eval_001\"],[\"previousHash\",\"sha256:chain_parties_genesis_0000000000000000000000000000000000000000\"],[\"parties\",[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"id\",\"timestamp\",\"method\",\"toolName\",\"namespace\",\"upstream\",\"principal\",\"durationMs\",\"success\",\"errorCode\",\"previousHash\"]},{\"party\":\"policy-engine\",\"role\":\"asserter\",\"scope\":[\"decisionContextDigest\"]}]]]",
526
+ "sha256_canonical": "5ff1b9a27985dbd920a44a81661e134a8f1b6968c0f30ac4fb027c5cb62fc10e",
527
+ "full_record_json": "{\"id\":\"rec_chain_pa_001\",\"timestamp\":\"2026-08-22T12:00:00.000Z\",\"method\":\"tools/call\",\"toolName\":\"fetch_data\",\"inputHash\":\"sha256:input_fetch\",\"outputHash\":\"sha256:output_fetch\",\"durationMs\":230,\"success\":true,\"errorCode\":null,\"previousHash\":\"sha256:chain_parties_genesis_0000000000000000000000000000000000000000\",\"namespace\":\"chain-party-test\",\"upstream\":\"https://api.example.com\",\"principal\":\"user:chain-tester\",\"decisionContextDigest\":\"sha256:policy_eval_001\",\"parties\":[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"id\",\"timestamp\",\"method\",\"toolName\",\"namespace\",\"upstream\",\"principal\",\"durationMs\",\"success\",\"errorCode\",\"previousHash\"]},{\"party\":\"policy-engine\",\"role\":\"asserter\",\"scope\":[\"decisionContextDigest\"]}]}",
528
+ "record_hash": "f154e23342cd33318799c1edc23507437d17a04451aad1a89f733afd3b74c195"
529
+ },
530
+ {
531
+ "record": {
532
+ "id": "rec_chain_pa_002",
533
+ "timestamp": "2026-08-22T12:00:01.500Z",
534
+ "method": "tools/call",
535
+ "toolName": "transform_data",
536
+ "inputHash": "sha256:input_transform",
537
+ "outputHash": "sha256:output_transform",
538
+ "durationMs": 85,
539
+ "success": true,
540
+ "errorCode": null,
541
+ "previousHash": "f154e23342cd33318799c1edc23507437d17a04451aad1a89f733afd3b74c195",
542
+ "namespace": "chain-party-test",
543
+ "upstream": null,
544
+ "principal": "user:chain-tester",
545
+ "decisionContextDigest": "sha256:policy_eval_002",
546
+ "parties": [
547
+ {
548
+ "party": "gateway",
549
+ "role": "witness",
550
+ "scope": [
551
+ "id",
552
+ "timestamp",
553
+ "method",
554
+ "toolName",
555
+ "namespace",
556
+ "upstream",
557
+ "principal",
558
+ "durationMs",
559
+ "success",
560
+ "errorCode",
561
+ "previousHash"
562
+ ]
563
+ },
564
+ {
565
+ "party": "policy-engine",
566
+ "role": "asserter",
567
+ "scope": [
568
+ "decisionContextDigest"
569
+ ]
570
+ }
571
+ ]
572
+ },
573
+ "canonical": "[[\"id\",\"rec_chain_pa_002\"],[\"timestamp\",\"2026-08-22T12:00:01.500Z\"],[\"method\",\"tools/call\"],[\"toolName\",\"transform_data\"],[\"namespace\",\"chain-party-test\"],[\"upstream\",null],[\"principal\",\"user:chain-tester\"],[\"durationMs\",85],[\"success\",true],[\"errorCode\",null],[\"decisionContextDigest\",\"sha256:policy_eval_002\"],[\"previousHash\",\"f154e23342cd33318799c1edc23507437d17a04451aad1a89f733afd3b74c195\"],[\"parties\",[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"id\",\"timestamp\",\"method\",\"toolName\",\"namespace\",\"upstream\",\"principal\",\"durationMs\",\"success\",\"errorCode\",\"previousHash\"]},{\"party\":\"policy-engine\",\"role\":\"asserter\",\"scope\":[\"decisionContextDigest\"]}]]]",
574
+ "sha256_canonical": "7583bd43ae7ca4eefb9bf143af5c3d77a79fde96bb0d98d1fdb68c94d6246c7b",
575
+ "full_record_json": "{\"id\":\"rec_chain_pa_002\",\"timestamp\":\"2026-08-22T12:00:01.500Z\",\"method\":\"tools/call\",\"toolName\":\"transform_data\",\"inputHash\":\"sha256:input_transform\",\"outputHash\":\"sha256:output_transform\",\"durationMs\":85,\"success\":true,\"errorCode\":null,\"previousHash\":\"f154e23342cd33318799c1edc23507437d17a04451aad1a89f733afd3b74c195\",\"namespace\":\"chain-party-test\",\"upstream\":null,\"principal\":\"user:chain-tester\",\"decisionContextDigest\":\"sha256:policy_eval_002\",\"parties\":[{\"party\":\"gateway\",\"role\":\"witness\",\"scope\":[\"id\",\"timestamp\",\"method\",\"toolName\",\"namespace\",\"upstream\",\"principal\",\"durationMs\",\"success\",\"errorCode\",\"previousHash\"]},{\"party\":\"policy-engine\",\"role\":\"asserter\",\"scope\":[\"decisionContextDigest\"]}]}",
576
+ "record_hash": "592254c9851b3a7fad4c17bef66f73d8b4bc58ea58d041c08e0ceff27f9d98c8",
577
+ "previous_record_hash": "f154e23342cd33318799c1edc23507437d17a04451aad1a89f733afd3b74c195"
578
+ }
579
+ ]
580
+ }
581
+ }
582
+ }