@agent-custody/receipts 0.1.7 → 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 CHANGED
@@ -256,6 +256,7 @@ The design is two producers feeding one verifier. The SDK is the top of the funn
256
256
  - SDK core: policy decision, record, and a generic `wrap(tool, fn)` for any framework whose tools are functions.
257
257
  - Claude Code command hook for PreToolUse, PostToolUse, and PostToolUseFailure, with blocking on deny.
258
258
  - Claude Agent SDK in-process hooks over the same handler.
259
+ - Provider-native deliveries: an upstream wrapping Stripe or GitHub attaches the signed webhook or delivery for the call; a verifier with the shared secret checks the HMAC, the timestamp, and the binding to the result, and reports the execution as attested by shared secret.
259
260
  - Logarithmic appends: the Merkle log caches complete subtrees, so issuing a receipt costs the same at the millionth leaf as at the first; measured at 0.15 ms per receipt and about half a millisecond per gateway call including policy, a fact lookup, and the upstream signature.
260
261
  - Retention on the log: `prune` replaces leaves older than a cutoff with their hashes and removes their bundles, so proofs still verify and the content is gone.
261
262
  - Several upstreams under one gateway and one grant, each tool owned by exactly one, with the receipt naming which served the call; consumed facts flow across them.
@@ -273,10 +274,9 @@ The design is two producers feeding one verifier. The SDK is the top of the funn
273
274
  **Next, in the order it pays off**
274
275
 
275
276
  1. OpenTelemetry export: emit each receipt as a span with the receipt id and issuer kind as attributes, so existing collectors and dashboards carry them without a new pipeline.
276
- 2. Provider-native upstream signatures (Stripe webhook signatures, GitHub delivery signatures) as adapters onto the upstream attestation field. [Issue #7](https://github.com/ch4r10t33r/agent-custody/issues/7).
277
- 3. An HTTP transport for the gateway, with the grant presented per connection, for a shared deployment rather than one process per agent session.
278
- 4. Delegation chains for sub-agents.
279
- 5. Receiver-attested receipts for agent-to-agent calls.
280
- 6. A TEE-hosted signer, then SD-JWT redaction, then ZK proofs of policy compliance. Not before.
277
+ 2. An HTTP transport for the gateway, with the grant presented per connection, for a shared deployment rather than one process per agent session.
278
+ 3. Delegation chains for sub-agents.
279
+ 4. Receiver-attested receipts for agent-to-agent calls.
280
+ 5. A TEE-hosted signer, then SD-JWT redaction, then ZK proofs of policy compliance. Not before.
281
281
 
282
282
  A Python SDK follows the same shape once the TypeScript adapters have settled.
package/dist/cli.js CHANGED
@@ -12,6 +12,13 @@ import { MerkleLog } from "./log.js";
12
12
  import { createSdkIssuer } from "./sdk/index.js";
13
13
  import { handleHookEvent } from "./sdk/claude.js";
14
14
  import { auditExtends, formatReport, verifyBundle } from "./verify.js";
15
+ /** A shared secret from an environment variable; never from the command line, where it would land in shell history. */
16
+ function secretFrom(envName) {
17
+ const v = process.env[envName];
18
+ if (!v)
19
+ throw new Error(`environment variable ${envName} is not set`);
20
+ return v;
21
+ }
15
22
  const USAGE = `agent-custody <command>
16
23
 
17
24
  keygen --dir <dir> --name <name>
@@ -22,7 +29,7 @@ const USAGE = `agent-custody <command>
22
29
  prune --log <log.jsonl> --before <ISO instant> [--receipts <dir>]
23
30
  retention on the receipt log: replaces older leaves with their hashes, so proofs still verify and the content is gone
24
31
  log --file <log.jsonl> --key <log.key> [--port 8787] [--host 127.0.0.1] [--token-env <NAME>] reference log server
25
- verify <bundle.json> --issuer-key <pub> [--principal-key <pub>] [--log-key <pub>] [--upstream-key <pub>] [--log <log.jsonl>] [--json]
32
+ verify <bundle.json> --issuer-key <pub> [--principal-key <pub>] [--log-key <pub>] [--upstream-key <pub>] [--stripe-secret-env NAME] [--github-secret-env NAME] [--log <log.jsonl>] [--json]
26
33
  audit --older <bundle.json> --newer <bundle.json> (--log <log.jsonl> | --log-url <url>) --issuer-key <pub> [--log-key <pub>] [--json]
27
34
  checks that the newer receipt's log extends the older one's: nothing between them was rewritten
28
35
  `;
@@ -133,6 +140,8 @@ async function main(argv) {
133
140
  "principal-key": { type: "string", multiple: true },
134
141
  "log-key": { type: "string", multiple: true },
135
142
  "upstream-key": { type: "string", multiple: true },
143
+ "stripe-secret-env": { type: "string" },
144
+ "github-secret-env": { type: "string" },
136
145
  log: { type: "string" },
137
146
  json: { type: "boolean", default: false },
138
147
  },
@@ -147,6 +156,7 @@ async function main(argv) {
147
156
  principalKeys: (values["principal-key"] ?? []).map(loadPublicKey),
148
157
  ...(values["log-key"] ? { logKeys: values["log-key"].map(loadPublicKey) } : {}),
149
158
  ...(values["upstream-key"] ? { upstreamKeys: values["upstream-key"].map(loadPublicKey) } : {}),
159
+ ...(values["stripe-secret-env"] || values["github-secret-env"] ? { providerSecrets: { ...(values["stripe-secret-env"] ? { stripe: secretFrom(values["stripe-secret-env"]) } : {}), ...(values["github-secret-env"] ? { github: secretFrom(values["github-secret-env"]) } : {}) } } : {}),
150
160
  ...(values.log ? { logFile: values.log } : {}),
151
161
  });
152
162
  console.log(values.json ? JSON.stringify(result, null, 2) : formatReport(result));
package/dist/gateway.js CHANGED
@@ -12,7 +12,7 @@ import { digestOf, loadPrivateKey, loadPublicKey } from "./crypto.js";
12
12
  import { delegationValidAt, verifyDelegation } from "./delegation.js";
13
13
  import { createIssuer } from "./issue.js";
14
14
  import { openLog } from "./log-sink.js";
15
- import { upstreamSignatureOf } from "./upstream.js";
15
+ import { upstreamEvidenceOf } from "./upstream.js";
16
16
  import { evaluate, policyDigest } from "./policy.js";
17
17
  export const GATEWAY_VERSION = "0.1.0";
18
18
  export const RECEIPT_META_KEY = "agent-custody/receipt";
@@ -164,8 +164,8 @@ export async function createGateway(cfg) {
164
164
  // state, such as the memory server, cites the receipt as the source of what it stores.
165
165
  const observed = Object.fromEntries(Object.entries(facts).map(([k, f]) => [k, f.value]));
166
166
  const result = await callUpstream(tool, args, { ...upstreamMeta, [OBSERVED_META_KEY]: observed });
167
- const upstreamSig = upstreamSignatureOf(result);
168
- execution = { status: result.isError ? "failed" : "executed", result, resultDigest: digestOf(result), provenance: "observed", ...(upstreamSig ? { upstream: { envelope: upstreamSig } } : {}) };
167
+ const evidence = upstreamEvidenceOf(result);
168
+ execution = { status: result.isError ? "failed" : "executed", result, resultDigest: digestOf(result), provenance: "observed", ...(evidence ? { upstream: evidence } : {}) };
169
169
  noteServedFacts(result);
170
170
  }
171
171
  catch (e) {
package/dist/receipt.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Envelope } from "./crypto.ts";
2
2
  import type { InclusionProof } from "./log.ts";
3
3
  import type { PolicyDecision } from "./policy.ts";
4
+ import type { UpstreamEvidence } from "./upstream.ts";
4
5
  export declare const RECEIPT_TYPE = "application/vnd.in-toto+json";
5
6
  export declare const RECEIPT_PREDICATE_TYPE = "https://agent-custody.dev/receipt/v0.2";
6
7
  export declare const TREEHEAD_TYPE = "application/vnd.agent-custody.treehead+json";
@@ -90,10 +91,8 @@ export interface ReceiptPredicate {
90
91
  result: unknown;
91
92
  resultDigest: string;
92
93
  provenance: Provenance;
93
- /** an upstream's own signature over what it returned, bound to this receipt; checked by a verifier holding the upstream's key */
94
- upstream?: {
95
- envelope: Envelope;
96
- };
94
+ /** an upstream's own signature over what it returned, bound to this receipt and checked with the upstream's key; or a provider's delivery, checked with the provider's shared secret */
95
+ upstream?: UpstreamEvidence;
97
96
  } | {
98
97
  status: "denied";
99
98
  reason: string;
@@ -25,3 +25,40 @@ export type UpstreamCheck = {
25
25
  };
26
26
  /** For verifiers: the envelope must verify against a trusted upstream key and bind to this receipt, tool, and content. */
27
27
  export declare function checkUpstream(envelope: Envelope, keys: PublicKeyRef[], expected: UpstreamAttestation): UpstreamCheck;
28
+ export interface ProviderAttestation {
29
+ provider: "stripe-webhook" | "github-delivery";
30
+ /** the delivery body exactly as received; the HMAC is over these bytes */
31
+ rawBody: string;
32
+ /** Stripe: the Stripe-Signature header; GitHub: the X-Hub-Signature-256 header */
33
+ signature: string;
34
+ /** dot path into the parsed body whose value must appear in the receipt's result, e.g. data.object.id */
35
+ bind: string;
36
+ /** GitHub: the X-GitHub-Delivery id, for the record */
37
+ deliveryId?: string;
38
+ }
39
+ export type UpstreamEvidence = {
40
+ envelope: Envelope;
41
+ } | ProviderAttestation;
42
+ export declare function isProviderAttestation(v: unknown): v is ProviderAttestation;
43
+ /** For upstreams wrapping a provider: attaches the provider's own delivery for this call. */
44
+ export declare function attachProviderAttestation<R extends CallToolResult>(result: R, attestation: ProviderAttestation): R;
45
+ /** For the gateway: whatever upstream evidence the result carries, a signed envelope or a provider delivery. */
46
+ export declare function upstreamEvidenceOf(result: CallToolResult): UpstreamEvidence | null;
47
+ export interface ProviderSecrets {
48
+ stripe?: string;
49
+ github?: string;
50
+ }
51
+ export interface ProviderCheckContext {
52
+ /** the receipt's timestamp, for Stripe's timestamp tolerance */
53
+ timestamp: string;
54
+ /** the receipt's execution result; the bound value must appear in it */
55
+ result: unknown;
56
+ /** seconds a Stripe timestamp may differ from the receipt's; default 300 */
57
+ toleranceSeconds?: number;
58
+ }
59
+ /** Stripe: header `t=<unix>,v1=<hex>`, HMAC-SHA256 over `<t>.<rawBody>`; GitHub: header `sha256=<hex>` over rawBody. */
60
+ export declare function checkProvider(att: ProviderAttestation, secrets: ProviderSecrets, ctx: ProviderCheckContext): UpstreamCheck;
61
+ /** For fake providers and tests: a Stripe-Signature header for a body at a time. */
62
+ export declare function stripeSignature(rawBody: string, secret: string, unixSeconds: number): string;
63
+ /** For fake providers and tests: an X-Hub-Signature-256 header for a body. */
64
+ export declare function githubSignature(rawBody: string, secret: string): string;
package/dist/upstream.js CHANGED
@@ -1,3 +1,8 @@
1
+ // Attested execution. An upstream that holds a key can sign what it returned, bound to the receipt the gateway is
2
+ // issuing, so the receipt's execution is no longer only what the gateway observed but what the upstream itself vouches
3
+ // for. The upstream puts a DSSE envelope on its result's _meta; the gateway embeds it; a verifier who trusts the
4
+ // upstream's key checks it. Provider-native formats, such as Stripe's webhook signatures, are adapters on top of this.
5
+ import { createHmac, timingSafeEqual } from "node:crypto";
1
6
  import { digestOf, dsseSign, dsseVerify } from "./crypto.js";
2
7
  export const UPSTREAM_SIG_META_KEY = "agent-custody/upstream-signature";
3
8
  export const UPSTREAM_TYPE = "application/vnd.agent-custody.upstream+json";
@@ -30,3 +35,66 @@ export function checkUpstream(envelope, keys, expected) {
30
35
  return { ok: false, error: "signed content differs from the result in the receipt" };
31
36
  return { ok: true, keyid: v.keyid };
32
37
  }
38
+ export function isProviderAttestation(v) {
39
+ const p = v;
40
+ return !!p && (p.provider === "stripe-webhook" || p.provider === "github-delivery") && typeof p.rawBody === "string" && typeof p.signature === "string" && typeof p.bind === "string";
41
+ }
42
+ /** For upstreams wrapping a provider: attaches the provider's own delivery for this call. */
43
+ export function attachProviderAttestation(result, attestation) {
44
+ return { ...result, _meta: { ...result._meta, [UPSTREAM_SIG_META_KEY]: attestation } };
45
+ }
46
+ /** For the gateway: whatever upstream evidence the result carries, a signed envelope or a provider delivery. */
47
+ export function upstreamEvidenceOf(result) {
48
+ const v = result._meta?.[UPSTREAM_SIG_META_KEY];
49
+ if (isProviderAttestation(v))
50
+ return v;
51
+ const env = upstreamSignatureOf(result);
52
+ return env ? { envelope: env } : null;
53
+ }
54
+ const pathValue = (body, path) => path.split(".").reduce((v, k) => (v && typeof v === "object" ? v[k] : undefined), body);
55
+ /** Stripe: header `t=<unix>,v1=<hex>`, HMAC-SHA256 over `<t>.<rawBody>`; GitHub: header `sha256=<hex>` over rawBody. */
56
+ export function checkProvider(att, secrets, ctx) {
57
+ const secret = att.provider === "stripe-webhook" ? secrets.stripe : secrets.github;
58
+ if (!secret)
59
+ return { ok: false, error: `no ${att.provider === "stripe-webhook" ? "Stripe" : "GitHub"} secret given` };
60
+ const hmac = (data) => createHmac("sha256", secret).update(data).digest("hex");
61
+ const equal = (a, b) => a.length === b.length && timingSafeEqual(Buffer.from(a), Buffer.from(b));
62
+ if (att.provider === "stripe-webhook") {
63
+ const parts = Object.fromEntries(att.signature.split(",").map((kv) => kv.split("=")));
64
+ const t = parts.t;
65
+ const v1 = parts.v1;
66
+ if (!t || !v1)
67
+ return { ok: false, error: "Stripe-Signature header lacks t or v1" };
68
+ if (!equal(hmac(`${t}.${att.rawBody}`), v1))
69
+ return { ok: false, error: "Stripe signature does not verify with this secret" };
70
+ const skew = Math.abs(Number(t) * 1000 - Date.parse(ctx.timestamp)) / 1000;
71
+ if (!(skew <= (ctx.toleranceSeconds ?? 300)))
72
+ return { ok: false, error: `Stripe timestamp is ${Math.round(skew)}s from the receipt, beyond tolerance` };
73
+ }
74
+ else {
75
+ const hex = att.signature.startsWith("sha256=") ? att.signature.slice(7) : "";
76
+ if (!hex || !equal(hmac(att.rawBody), hex))
77
+ return { ok: false, error: "GitHub signature does not verify with this secret" };
78
+ }
79
+ let body;
80
+ try {
81
+ body = JSON.parse(att.rawBody);
82
+ }
83
+ catch {
84
+ return { ok: false, error: "delivery body is not JSON" };
85
+ }
86
+ const bound = pathValue(body, att.bind);
87
+ if (bound === undefined || bound === null || bound === "")
88
+ return { ok: false, error: `delivery has no value at ${att.bind}` };
89
+ if (!JSON.stringify(ctx.result).includes(JSON.stringify(bound).replace(/^"|"$/g, "")))
90
+ return { ok: false, error: `delivery's ${att.bind} (${String(bound)}) does not appear in the receipt's result` };
91
+ return { ok: true, keyid: `shared secret (${att.provider}, bound on ${att.bind})` };
92
+ }
93
+ /** For fake providers and tests: a Stripe-Signature header for a body at a time. */
94
+ export function stripeSignature(rawBody, secret, unixSeconds) {
95
+ return `t=${unixSeconds},v1=${createHmac("sha256", secret).update(`${unixSeconds}.${rawBody}`).digest("hex")}`;
96
+ }
97
+ /** For fake providers and tests: an X-Hub-Signature-256 header for a body. */
98
+ export function githubSignature(rawBody, secret) {
99
+ return `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
100
+ }
package/dist/verify.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type Envelope, type PublicKeyRef } from "./crypto.ts";
2
+ import { type ProviderSecrets } from "./upstream.ts";
2
3
  import { type ReceiptBundle, type ReceiptStatement, type TreeHead } from "./receipt.ts";
3
4
  export interface Check {
4
5
  name: string;
@@ -13,6 +14,8 @@ export interface VerifyOptions {
13
14
  logKeys?: PublicKeyRef[];
14
15
  /** keys of upstreams that sign their results; with one given, an execution carrying an upstream signature is checked and becomes attested */
15
16
  upstreamKeys?: PublicKeyRef[];
17
+ /** shared secrets for provider-native deliveries; with the matching one given, an execution carrying a Stripe or GitHub delivery is checked */
18
+ providerSecrets?: ProviderSecrets;
16
19
  /** If given, the root is recomputed from this log file at the receipt's tree size and compared. */
17
20
  logFile?: string;
18
21
  }
package/dist/verify.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { canonicalize, digestOf, dsseVerify } from "./crypto.js";
3
3
  import { delegationValidAt, verifyDelegation } from "./delegation.js";
4
4
  import { leafHash, MerkleLog, verifyConsistency, verifyInclusion } from "./log.js";
5
- import { checkUpstream, contentDigest } from "./upstream.js";
5
+ import { checkProvider, checkUpstream, contentDigest, isProviderAttestation } from "./upstream.js";
6
6
  import { RECEIPT_PREDICATE_TYPE, RECEIPT_TYPE, TREEHEAD_TYPE } from "./receipt.js";
7
7
  const short = (s) => s.slice(0, 12);
8
8
  export function verifyBundle(bundle, opts) {
@@ -45,10 +45,18 @@ export function verifyBundle(bundle, opts) {
45
45
  add("principal is claimed, not attested", p.principal.provenance === "claimed", "no signed delegation in this receipt");
46
46
  }
47
47
  add("request args digest", digestOf(p.request.args) === p.request.argsDigest && st.subject[0]?.digest.sha256 === p.request.argsDigest);
48
- if ((p.execution.status === "executed" || p.execution.status === "failed") && p.execution.upstream && (opts.upstreamKeys?.length ?? 0) > 0) {
48
+ if ((p.execution.status === "executed" || p.execution.status === "failed") && p.execution.upstream) {
49
49
  const result = p.execution.result;
50
- const u = checkUpstream(p.execution.upstream.envelope, opts.upstreamKeys, { receiptId: p.receiptId, tool: p.tool.name, contentDigest: contentDigest(result) });
51
- add("upstream signature (upstream key)", u.ok, u.ok ? `keyid ${short(u.keyid)}` : u.error);
50
+ if (isProviderAttestation(p.execution.upstream)) {
51
+ if (opts.providerSecrets) {
52
+ const u = checkProvider(p.execution.upstream, opts.providerSecrets, { timestamp: p.timestamp, result });
53
+ add("upstream signature (provider secret)", u.ok, u.ok ? u.keyid : u.error);
54
+ }
55
+ }
56
+ else if ((opts.upstreamKeys?.length ?? 0) > 0) {
57
+ const u = checkUpstream(p.execution.upstream.envelope, opts.upstreamKeys, { receiptId: p.receiptId, tool: p.tool.name, contentDigest: contentDigest(result) });
58
+ add("upstream signature (upstream key)", u.ok, u.ok ? `keyid ${short(u.keyid)}` : u.error);
59
+ }
52
60
  }
53
61
  if (p.policy) {
54
62
  const consistent = p.policy.decision === "allow" ? p.execution.status !== "denied" : p.execution.status === "denied";
@@ -137,8 +145,11 @@ export function formatReport(r) {
137
145
  row("policy", "-", "(none evaluated)");
138
146
  if (p.consumed)
139
147
  row("consumed", p.consumed.provenance, p.consumed.factIds.length === 0 ? "(no facts shown before this call)" : p.consumed.factIds);
140
- const upstreamCheck = r.checks.find((c) => c.name === "upstream signature (upstream key)");
148
+ const upstreamCheck = r.checks.find((c) => c.name === "upstream signature (upstream key)" || c.name === "upstream signature (provider secret)");
141
149
  const hasUpstream = (p.execution.status === "executed" || p.execution.status === "failed") && !!p.execution.upstream;
142
- row("execution", upstreamCheck?.ok ? "attested" : p.execution.provenance, `${p.execution.status}${hasUpstream ? (upstreamCheck ? (upstreamCheck.ok ? ` (signed by upstream ${upstreamCheck.detail})` : " (upstream signature FAILED)") : " (carries an upstream signature; pass --upstream-key to check it)") : ""}`);
150
+ const byProvider = hasUpstream && isProviderAttestation(p.execution.upstream);
151
+ const attestedAs = upstreamCheck?.ok ? (byProvider ? "attested (shared secret)" : "attested") : p.execution.provenance;
152
+ const note = !hasUpstream ? "" : upstreamCheck ? (upstreamCheck.ok ? ` (${byProvider ? upstreamCheck.detail : `signed by upstream ${upstreamCheck.detail}`})` : " (upstream signature FAILED)") : byProvider ? " (carries a provider delivery; pass the provider secret to check it)" : " (carries an upstream signature; pass --upstream-key to check it)";
153
+ row("execution", attestedAs, `${p.execution.status}${note}`);
143
154
  return lines.join("\n");
144
155
  }
@@ -171,7 +171,17 @@ 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. Provider-native signatures, such as Stripe's webhook signatures, are adapters on top of the same field and are not implemented yet.
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.
175
185
 
176
186
  ## Retention on the log
177
187
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-custody/receipts",
3
- "version": "0.1.7",
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": {
@@ -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": "eyJyb290SGFzaCI6IjY4ODlmODQyNmVkODg4YTVlNzI0Y2YzNThlNmI0Y2E3ZmI3YWVmMTkwODkxOWE5MGYxZjNlNzFjMWU3MmNkODUiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkyOVoiLCJ0cmVlU2l6ZSI6MX0=",
10
+ "payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
11
11
  "signatures": [
12
12
  {
13
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
14
- "sig": "FL7TL3y1JgXinAkxywmKfoULNe2V8D+JpIcsJWok54rIun70z7bmj78R1VnNe9aADLSGQb0JTYhdaODn9mOnCg=="
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": "eyJyb290SGFzaCI6IjUxY2MyNzIzMzM0NzhmMjYxNDU1MWFmMWM1NjExMzBjOTBlODEwM2JhYmI1NzNkZjY2YjM3ZTVhMDk1YjI0ZWYiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkzM1oiLCJ0cmVlU2l6ZSI6Mn0=",
20
+ "payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
21
21
  "signatures": [
22
22
  {
23
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
24
- "sig": "o/zrDH6/I3VPabSVS4RozKm945Nd+hiwt6JTytpyMavxizlR8ZuGmG6lX56+A0M4wAk/H0qmmHXoLCGuobhdCw=="
23
+ "keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
24
+ "sig": "++9Ra+7i8vejqY418WnWH4CXA9Rxgb/gDHWrEWgZLYpSPjOdbEXB3XlEArIKaP3+I19eTjK4EQQ07mI2WM3ZCQ=="
25
25
  }
26
26
  ]
27
27
  },
28
28
  "proof": [
29
- "e74d1e2874003df276b93e2e764da876201bcc1a9efe8752118676eafc4ce5eb"
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": "eyJyb290SGFzaCI6IjUxY2MyNzIzMzM0NzhmMjYxNDU1MWFmMWM1NjExMzBjOTBlODEwM2JhYmI1NzNkZjY2YjM3ZTVhMDk1YjI0ZWYiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkzM1oiLCJ0cmVlU2l6ZSI6Mn0=",
44
+ "payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
45
45
  "signatures": [
46
46
  {
47
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
48
- "sig": "o/zrDH6/I3VPabSVS4RozKm945Nd+hiwt6JTytpyMavxizlR8ZuGmG6lX56+A0M4wAk/H0qmmHXoLCGuobhdCw=="
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": "eyJyb290SGFzaCI6IjY4ODlmODQyNmVkODg4YTVlNzI0Y2YzNThlNmI0Y2E3ZmI3YWVmMTkwODkxOWE5MGYxZjNlNzFjMWU3MmNkODUiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkyOVoiLCJ0cmVlU2l6ZSI6MX0=",
54
+ "payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
55
55
  "signatures": [
56
56
  {
57
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
58
- "sig": "FL7TL3y1JgXinAkxywmKfoULNe2V8D+JpIcsJWok54rIun70z7bmj78R1VnNe9aADLSGQb0JTYhdaODn9mOnCg=="
57
+ "keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
58
+ "sig": "ZcTaC2tLzE32o+sIj0qFr9lRWaidilkLf6VkVTt6N3McGnp/lISpE0f+naKlRvvQR70kreVm43mwHeMnLBUPCw=="
59
59
  }
60
60
  ]
61
61
  },
62
62
  "proof": [
63
- "e74d1e2874003df276b93e2e764da876201bcc1a9efe8752118676eafc4ce5eb"
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": "eyJyb290SGFzaCI6IjY4ODlmODQyNmVkODg4YTVlNzI0Y2YzNThlNmI0Y2E3ZmI3YWVmMTkwODkxOWE5MGYxZjNlNzFjMWU3MmNkODUiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkyOVoiLCJ0cmVlU2l6ZSI6MX0=",
80
+ "payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
81
81
  "signatures": [
82
82
  {
83
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
84
- "sig": "FL7TL3y1JgXinAkxywmKfoULNe2V8D+JpIcsJWok54rIun70z7bmj78R1VnNe9aADLSGQb0JTYhdaODn9mOnCg=="
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": "eyJyb290SGFzaCI6IjUxY2MyNzIzMzM0NzhmMjYxNDU1MWFmMWM1NjExMzBjOTBlODEwM2JhYmI1NzNkZjY2YjM3ZTVhMDk1YjI0ZWYiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkzM1oiLCJ0cmVlU2l6ZSI6Mn0=",
90
+ "payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
91
91
  "signatures": [
92
92
  {
93
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
94
- "sig": "o/zrDH6/I3VPabSVS4RozKm945Nd+hiwt6JTytpyMavxizlR8ZuGmG6lX56+A0M4wAk/H0qmmHXoLCGuobhdCw=="
93
+ "keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
94
+ "sig": "++9Ra+7i8vejqY418WnWH4CXA9Rxgb/gDHWrEWgZLYpSPjOdbEXB3XlEArIKaP3+I19eTjK4EQQ07mI2WM3ZCQ=="
95
95
  }
96
96
  ]
97
97
  },
98
98
  "proof": [
99
- "e74d1e2874003df276b93e2e764da876201bcc1a9efe8752118676eafc4ce5eb"
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": "eyJyb290SGFzaCI6IjY4ODlmODQyNmVkODg4YTVlNzI0Y2YzNThlNmI0Y2E3ZmI3YWVmMTkwODkxOWE5MGYxZjNlNzFjMWU3MmNkODUiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkyOVoiLCJ0cmVlU2l6ZSI6MX0=",
117
+ "payload": "eyJyb290SGFzaCI6ImFmYjZhMmJjNThiZmQwNDk4YWNjZTZiYTY1MTAxM2IwY2JiYjlmNTI1MWM1NTI0Nzg3NGY5ZWYzOGM3YjAxMjMiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ0OVoiLCJ0cmVlU2l6ZSI6MX0=",
118
118
  "signatures": [
119
119
  {
120
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
121
- "sig": "FL7TL3y1JgXinAkxywmKfoULNe2V8D+JpIcsJWok54rIun70z7bmj78R1VnNe9aADLSGQb0JTYhdaODn9mOnCg=="
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": "eyJyb290SGFzaCI6IjUxY2MyNzIzMzM0NzhmMjYxNDU1MWFmMWM1NjExMzBjOTBlODEwM2JhYmI1NzNkZjY2YjM3ZTVhMDk1YjI0ZWYiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDA4OjM3OjM0LjkzM1oiLCJ0cmVlU2l6ZSI6Mn0=",
127
+ "payload": "eyJyb290SGFzaCI6IjJlZTc2MmUzYzdlYzdhODE4ODc4MjUwNDhiZGMwZTEyMWFjNDQzM2VjM2FlNjFjYjU5MjM3MWNmYWMxNDVjZmEiLCJ0aW1lc3RhbXAiOiIyMDI2LTA5LTA3VDEzOjQ4OjQyLjQ1M1oiLCJ0cmVlU2l6ZSI6Mn0=",
128
128
  "signatures": [
129
129
  {
130
- "keyid": "48967bd6051d66cebf961f226a6ae4e90dce91ead9ac10c0f9aaa427d40c4c16",
131
- "sig": "o/zrDH6/I3VPabSVS4RozKm945Nd+hiwt6JTytpyMavxizlR8ZuGmG6lX56+A0M4wAk/H0qmmHXoLCGuobhdCw=="
130
+ "keyid": "402c5fa71d8055eadffe3e33d7847e543551d24c002a9adf86286f03eb5e4538",
131
+ "sig": "++9Ra+7i8vejqY418WnWH4CXA9Rxgb/gDHWrEWgZLYpSPjOdbEXB3XlEArIKaP3+I19eTjK4EQQ07mI2WM3ZCQ=="
132
132
  }
133
133
  ]
134
134
  },
@@ -53,18 +53,18 @@
53
53
  }
54
54
  ],
55
55
  "keyid": {
56
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEADBTs73puK6sTeOys85s6w1yJCBPt+eiQheOIszKqBiU=\n-----END PUBLIC KEY-----\n",
57
- "keyid": "9dd265bc328e448488e35ec8ca89cc3b4270bfc6c8a0ba01845bb8867f1c7c60"
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-----\nMCowBQYDK2VwAyEADBTs73puK6sTeOys85s6w1yJCBPt+eiQheOIszKqBiU=\n-----END PUBLIC KEY-----\n",
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": "9dd265bc328e448488e35ec8ca89cc3b4270bfc6c8a0ba01845bb8867f1c7c60",
67
- "sig": "6zlQBbpy7oGNtH2d5gJ5ZmHzrKIjF8gT0XDkjhzafjCWWphFCcyZaVzZumJOjl6hnhzvu73FgHenAjyImMA0Cg=="
66
+ "keyid": "b8f05ee956d16bc7e981e1cc6e62739623e3a169d8e14aa43d2c2b32ce956b44",
67
+ "sig": "sSViplEmHbNqomtr8I67F+jIH8JDbmFhGkioDN8nXxpb1yg21/FLP2wqBVHrTMveuCtF4O+EoxO7dlBITeoIBw=="
68
68
  }
69
69
  ]
70
70
  },