@agent-custody/receipts 0.1.6 → 0.1.8
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 +9 -6
- package/dist/cli.js +24 -1
- package/dist/gateway.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.js +63 -31
- package/dist/receipt.d.ts +3 -4
- package/dist/retention.d.ts +14 -0
- package/dist/retention.js +54 -0
- package/dist/upstream.d.ts +37 -0
- package/dist/upstream.js +68 -0
- package/dist/verify.d.ts +3 -0
- package/dist/verify.js +17 -6
- package/docs/policies.md +63 -0
- package/docs/verification.md +21 -1
- package/package.json +5 -1
- package/vectors/audit.json +27 -27
- package/vectors/canonical.json +5 -5
- package/vectors/receipts.json +288 -137
package/docs/verification.md
CHANGED
|
@@ -171,5 +171,25 @@ The remote log also serves `GET /head`, its current tree head signed with the lo
|
|
|
171
171
|
|
|
172
172
|
On a gateway receipt the execution is `observed`: the gateway saw what the upstream returned. An upstream that holds a key can do better and sign its result for the receipt being issued; the gateway embeds the signature, and a verifier given the upstream's public key checks it and reports the execution as `attested` by that key. The check binds the signature to the receipt id, the tool, and the digest of the result content, so a signature cannot be moved between receipts.
|
|
173
173
|
|
|
174
|
-
For upstream authors, `signResult(result, key, receiptId, tool)` from `@agent-custody/receipts` does the signing; the receipt id arrives in the call's `_meta["agent-custody/receipt"]`. The memory server in `@agent-custody/state` signs when started with `--key`, and the demo's fake upstream does too.
|
|
174
|
+
For upstream authors, `signResult(result, key, receiptId, tool)` from `@agent-custody/receipts` does the signing; the receipt id arrives in the call's `_meta["agent-custody/receipt"]`. The memory server in `@agent-custody/state` signs when started with `--key`, and the demo's fake upstream does too.
|
|
175
|
+
|
|
176
|
+
### Provider-native deliveries
|
|
177
|
+
|
|
178
|
+
Real providers do not sign per receipt. Stripe signs webhooks and GitHub signs deliveries with an HMAC over the raw body under a shared secret, unbound to any receipt. An MCP server wrapping such a provider can attach the delivery that corresponds to the call, with `attachProviderAttestation(result, { provider: "stripe-webhook", rawBody, signature, bind: "data.object.id" })`, and the gateway embeds it as `execution.upstream`. A verifier given the secret recomputes the HMAC, checks Stripe's timestamp against the receipt's within five minutes, and checks that the value at `bind` in the delivery appears in the receipt's result, which is what ties a delivery to this call.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
STRIPE_WEBHOOK_SECRET=whsec_... node src/cli.ts verify receipts/<id>.json --issuer-key keys/gateway.pub --principal-key keys/principal.pub --stripe-secret-env STRIPE_WEBHOOK_SECRET
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The report then says `attested (shared secret)`, deliberately distinct from `attested`: anyone holding the secret could forge a delivery, so this is the provider's word as far as the secret is trusted, not a signature only the provider could make. Without the secret the delivery is carried and not checked, and the report says so. The demo's fake Stripe attaches webhooks when started with `--webhook-secret`; the conformance vectors include a receipt verified with and without the secret.
|
|
185
|
+
|
|
186
|
+
## Retention on the log
|
|
187
|
+
|
|
188
|
+
Receipts hold values: request arguments, results, facts. The log is append-only and hashed, so nothing can simply be deleted from it. `prune` is how retention reaches it without breaking a proof:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
node src/cli.ts prune --log log.jsonl --before 2026-06-01T00:00:00Z --receipts receipts
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Every leaf whose receipt is older than the cutoff is replaced in the file by its leaf hash, and the receipt's bundle file is deleted. The Merkle tree is built from leaf hashes, so every root, every inclusion proof, and every consistency proof for the remaining leaves is unchanged, and `--log` verification of later receipts still passes. Someone who kept a pruned receipt's bundle can still prove it was in the log; nobody holding only the log can recover what it said. Run it on the same schedule as memory retention.
|
|
175
195
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-custody/receipts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Chain of custody for AI agents: signed, independently verifiable receipts for tool calls. MCP gateway + Cedar policy + Merkle transparency log",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"./sdk/langchain": {
|
|
40
40
|
"types": "./dist/sdk/langchain.d.ts",
|
|
41
41
|
"default": "./dist/sdk/langchain.js"
|
|
42
|
+
},
|
|
43
|
+
"./cli": {
|
|
44
|
+
"types": "./dist/cli.d.ts",
|
|
45
|
+
"default": "./dist/cli.js"
|
|
42
46
|
}
|
|
43
47
|
},
|
|
44
48
|
"files": [
|
package/vectors/audit.json
CHANGED
|
@@ -7,26 +7,26 @@
|
|
|
7
7
|
"description": "The tree head from the first remote receipt and the log's later head, with the proof the log served.",
|
|
8
8
|
"older": {
|
|
9
9
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
10
|
-
"payload": "
|
|
10
|
+
"payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
11
11
|
"signatures": [
|
|
12
12
|
{
|
|
13
|
-
"keyid": "
|
|
14
|
-
"sig": "
|
|
13
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
14
|
+
"sig": "ZcTaC2tLzE32o+sIj0qFr9lRWaidilkLf6VkVTt6N3McGnp/lISpE0f+naKlRvvQR70kreVm43mwHeMnLBUPCw=="
|
|
15
15
|
}
|
|
16
16
|
]
|
|
17
17
|
},
|
|
18
18
|
"newer": {
|
|
19
19
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
20
|
-
"payload": "
|
|
20
|
+
"payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
21
21
|
"signatures": [
|
|
22
22
|
{
|
|
23
|
-
"keyid": "
|
|
24
|
-
"sig": "
|
|
23
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
24
|
+
"sig": "++9Ra+7i8vejqY418WnWH4CXA9Rxgb/gDHWrEWgZLYpSPjOdbEXB3XlEArIKaP3+I19eTjK4EQQ07mI2WM3ZCQ=="
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
28
|
"proof": [
|
|
29
|
-
"
|
|
29
|
+
"ae2d0fcc4034cc32db9e07e9f039358baaef3ae6e22533b9c273ee9042a51d5b"
|
|
30
30
|
],
|
|
31
31
|
"keys": [
|
|
32
32
|
"log"
|
|
@@ -41,26 +41,26 @@
|
|
|
41
41
|
"description": "The same heads the wrong way round.",
|
|
42
42
|
"older": {
|
|
43
43
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
44
|
-
"payload": "
|
|
44
|
+
"payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
45
45
|
"signatures": [
|
|
46
46
|
{
|
|
47
|
-
"keyid": "
|
|
48
|
-
"sig": "
|
|
47
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
48
|
+
"sig": "++9Ra+7i8vejqY418WnWH4CXA9Rxgb/gDHWrEWgZLYpSPjOdbEXB3XlEArIKaP3+I19eTjK4EQQ07mI2WM3ZCQ=="
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
52
|
"newer": {
|
|
53
53
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
54
|
-
"payload": "
|
|
54
|
+
"payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
55
55
|
"signatures": [
|
|
56
56
|
{
|
|
57
|
-
"keyid": "
|
|
58
|
-
"sig": "
|
|
57
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
58
|
+
"sig": "ZcTaC2tLzE32o+sIj0qFr9lRWaidilkLf6VkVTt6N3McGnp/lISpE0f+naKlRvvQR70kreVm43mwHeMnLBUPCw=="
|
|
59
59
|
}
|
|
60
60
|
]
|
|
61
61
|
},
|
|
62
62
|
"proof": [
|
|
63
|
-
"
|
|
63
|
+
"ae2d0fcc4034cc32db9e07e9f039358baaef3ae6e22533b9c273ee9042a51d5b"
|
|
64
64
|
],
|
|
65
65
|
"keys": [
|
|
66
66
|
"log"
|
|
@@ -77,26 +77,26 @@
|
|
|
77
77
|
"description": "Tree heads checked against the app key, which did not sign them.",
|
|
78
78
|
"older": {
|
|
79
79
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
80
|
-
"payload": "
|
|
80
|
+
"payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
81
81
|
"signatures": [
|
|
82
82
|
{
|
|
83
|
-
"keyid": "
|
|
84
|
-
"sig": "
|
|
83
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
84
|
+
"sig": "ZcTaC2tLzE32o+sIj0qFr9lRWaidilkLf6VkVTt6N3McGnp/lISpE0f+naKlRvvQR70kreVm43mwHeMnLBUPCw=="
|
|
85
85
|
}
|
|
86
86
|
]
|
|
87
87
|
},
|
|
88
88
|
"newer": {
|
|
89
89
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
90
|
-
"payload": "
|
|
90
|
+
"payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
91
91
|
"signatures": [
|
|
92
92
|
{
|
|
93
|
-
"keyid": "
|
|
94
|
-
"sig": "
|
|
93
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
94
|
+
"sig": "++9Ra+7i8vejqY418WnWH4CXA9Rxgb/gDHWrEWgZLYpSPjOdbEXB3XlEArIKaP3+I19eTjK4EQQ07mI2WM3ZCQ=="
|
|
95
95
|
}
|
|
96
96
|
]
|
|
97
97
|
},
|
|
98
98
|
"proof": [
|
|
99
|
-
"
|
|
99
|
+
"ae2d0fcc4034cc32db9e07e9f039358baaef3ae6e22533b9c273ee9042a51d5b"
|
|
100
100
|
],
|
|
101
101
|
"keys": [
|
|
102
102
|
"app"
|
|
@@ -114,21 +114,21 @@
|
|
|
114
114
|
"description": "A proof with a hash removed.",
|
|
115
115
|
"older": {
|
|
116
116
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
117
|
-
"payload": "
|
|
117
|
+
"payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
|
|
118
118
|
"signatures": [
|
|
119
119
|
{
|
|
120
|
-
"keyid": "
|
|
121
|
-
"sig": "
|
|
120
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
121
|
+
"sig": "ZcTaC2tLzE32o+sIj0qFr9lRWaidilkLf6VkVTt6N3McGnp/lISpE0f+naKlRvvQR70kreVm43mwHeMnLBUPCw=="
|
|
122
122
|
}
|
|
123
123
|
]
|
|
124
124
|
},
|
|
125
125
|
"newer": {
|
|
126
126
|
"payloadType": "application/vnd.agent-custody.treehead+json",
|
|
127
|
-
"payload": "
|
|
127
|
+
"payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
|
|
128
128
|
"signatures": [
|
|
129
129
|
{
|
|
130
|
-
"keyid": "
|
|
131
|
-
"sig": "
|
|
130
|
+
"keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
|
|
131
|
+
"sig": "++9Ra+7i8vejqY418WnWH4CXA9Rxgb/gDHWrEWgZLYpSPjOdbEXB3XlEArIKaP3+I19eTjK4EQQ07mI2WM3ZCQ=="
|
|
132
132
|
}
|
|
133
133
|
]
|
|
134
134
|
},
|
package/vectors/canonical.json
CHANGED
|
@@ -53,18 +53,18 @@
|
|
|
53
53
|
}
|
|
54
54
|
],
|
|
55
55
|
"keyid": {
|
|
56
|
-
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\
|
|
57
|
-
"keyid": "
|
|
56
|
+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAPkUEkHrnkfYttWXvl/rsSvOqIW8k0HvjQknUWhsI0tI=\n-----END PUBLIC KEY-----\n",
|
|
57
|
+
"keyid": "b8f05ee956d16bc7e981e1cc6e62739623e3a169d8e14aa43d2c2b32ce956b44"
|
|
58
58
|
},
|
|
59
59
|
"dsse": {
|
|
60
|
-
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\
|
|
60
|
+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAPkUEkHrnkfYttWXvl/rsSvOqIW8k0HvjQknUWhsI0tI=\n-----END PUBLIC KEY-----\n",
|
|
61
61
|
"envelope": {
|
|
62
62
|
"payloadType": "application/vnd.example+json",
|
|
63
63
|
"payload": "eyJoZWxsbyI6IndvcmxkIn0=",
|
|
64
64
|
"signatures": [
|
|
65
65
|
{
|
|
66
|
-
"keyid": "
|
|
67
|
-
"sig": "
|
|
66
|
+
"keyid": "b8f05ee956d16bc7e981e1cc6e62739623e3a169d8e14aa43d2c2b32ce956b44",
|
|
67
|
+
"sig": "sSViplEmHbNqomtr8I67F+jIH8JDbmFhGkioDN8nXxpb1yg21/FLP2wqBVHrTMveuCtF4O+EoxO7dlBITeoIBw=="
|
|
68
68
|
}
|
|
69
69
|
]
|
|
70
70
|
},
|