@agent-custody/receipts 0.5.9 → 0.6.1

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/docs/usage.md CHANGED
@@ -42,6 +42,8 @@ node src/cli.ts grant \
42
42
 
43
43
  The gateway refuses to start if the grant is outside its validity window, and every receipt records the grant so a verifier can re-check it.
44
44
 
45
+ **Sub-agents.** A grant that names the agent's own public key (`--agent-key agent.pub`, carried as `agentKey`) lets that agent delegate: `agent-custody delegate --key agent.key --parent grant.json --agent refunder --scopes stripe.refund --out refunder.json` signs a narrower grant for the sub-agent with the parent grant embedded. The chain may go three delegations deep. A verifier walks it back to the principal: every link must be signed by the key its parent names, every scope must be one the parent holds, every window must sit inside the parent's, and the principal never changes; the gateway applies the same rule before opening a session, and the receipt names the sub-agent as the agent and the principal as the principal, with the whole chain inside, so `verify` reports `delegation chain to the principal: user_456 → planner → refunder`. Nothing about the tools changes: the sub-agent sees the scopes its own grant names and no more.
46
+
45
47
  **3. Write a policy.** A Cedar file. Default is deny. See [policies.md](policies.md).
46
48
 
47
49
  ```cedar
@@ -105,7 +107,7 @@ An upstream need not be an MCP server. A plain HTTP API is described as tools:
105
107
  "log": { "url": "https://log.example.com/", "tokenEnv": "AGENT_CUSTODY_LOG_TOKEN" }
106
108
  ```
107
109
 
108
- Exactly one of the two. The bearer token comes from the named environment variable, never from the file, and a missing variable fails at startup. Add `"hashOnly": true` for any log run by someone else: the gateway then sends only the leaf hash, sha256 of the receipt envelope with the RFC 6962 prefix, so the log commits to the receipt without ever holding it, and the receipts with their arguments and results stay in `receiptsDir`. The verifier does not change; it hashes the envelope itself. A log that serves several tenants is reached at `<url>/t/<tenant>/`, and each of its tree heads names its log, which a verifier checks with `--log-id`. With a remote log the tree head in each receipt is signed by the log's key, and a verifier must be given that key with `--log-key`. An append that gets no answer within `timeoutMs` (default 10000) counts as unreachable and is retried like a server error, so a log that accepts connections and never answers cannot hold a call forever. If the log refuses a leaf, the receipt is not issued and the call returns an error to the agent. For an ordinary call the upstream action has already happened by then, and the error says so; a receipt that was never logged must not be handed out. For a tool named in `precommit` the order is reversed, below, and the action never happens. The reference log server is `node src/cli.ts log --file log.jsonl --key keys/log.key --port 8787 --token-env AGENT_CUSTODY_LOG_TOKEN [--log-id <id>] [--tenants tenants.json]`. It serves `POST /append` with `{leaf}` or `{leafHash}` (token required when one is configured), `GET /root?size=N`, `GET /consistency?old=M&new=N`, and `GET /head`; [verification.md](verification.md) says what each proves. `--log-id` writes that id into every tree head. `--tenants` names a JSON file, `{ "acme": { "file": "acme.jsonl", "tokenEnv": "ACME_TOKEN", "logId": "acme-eu" } }`, and each tenant is its own log at `/t/acme/…` with its own token and id; the default log stays at the root paths. With `--db-env DATABASE_URL` the server keeps its logs in Postgres instead of files, and needs the `pg` package beside it: leaves as hashes in one table keyed by tenant, one writer per tenant enforced with an advisory lock so a second instance is safe, tenants and their tokens in tables of their own with tokens stored only as hashes, and rate limits per token (50 appends a second, burst 100, a 64 KB body cap; a refused append answers 429 with `retry-after`, and the gateway's sink retries a few times). Tenants are managed with `log-admin --db-env DATABASE_URL`: `tenant add <id> [--log-id <id>]`, `token add <tenant> --label <text>` (the token is printed once), `token revoke <tenant> <hash-prefix>`, `tenant disable <id>`, and `import --file log.jsonl [--tenant default]` to bring an existing file log in as hashes. The root paths serve the tenant `default`, created on first start with `--log-id`, and `--token-env` still works for it. The key that signs tree heads can live in its own process: `agent-custody signer --key keys/log.key --port 8790 --token-env SIGNER_TOKEN` holds it and answers `POST /sign` with the shared secret and `GET /keys` to anyone; the log server then runs with `--signer-url http://signer:8790/ --signer-token-env SIGNER_TOKEN` instead of `--key`, and the process that faces the internet never holds the key. Either way the log serves its keys at `/.well-known/agent-custody-log.json`, current key first and retired keys (`--retired-key old.pub`) after it, so verifiers fetch and pin them with `verify --log-url` and `audit --log-url` rather than receiving a key file from the operator. With `--checkpoint-dir <dir>` the server publishes a signed checkpoint, every `--checkpoint-every` seconds (default 300), for each log whose tree has grown, and every `--checkpoint-heartbeat` seconds (default 21600, six hours) for a log that has not, so a quiet log's latest checkpoint is never more than six hours old and the monitor can tell quiet from stalled, as `<dir>/<tenant>/<treeSize>.json` and `latest.json`, and with a database also as rows; `GET /checkpoints?since=<size>` and `GET /t/<tenant>/checkpoints` list them. Serve the directory read-only from a second host, so the record of what the log signed does not depend on the log's API being up; a verifier who kept an earlier head audits against a later checkpoint with `audit --older <bundle> --newer <checkpoint> --log-url <url>`. With `--admin-token-env ADMIN_TOKEN` (Postgres only) the server also serves the operator's page at `/admin` and its API under `/admin/`: list and create tenants, mint a token that is shown once beside the tenant's welcome sheet, revoke tokens, disable tenants. Everything under `/admin`, the page included, needs the admin token: the browser asks for it (any user name, the token as the password) and an API client sends it as a bearer; a handful of wrong attempts from one address are throttled for a minute. Every change made there or with `log-admin` is recorded: who (the name entered at the browser prompt and the address, `bearer` for an API client, or the user and host for the command line), what (`tenant.add`, `tenant.disable`, `token.add`, `token.revoke`), which tenant, and the detail, never the token itself; the page shows it under Activity, `GET /admin/audit?tenant=&limit=` and `log-admin audit` list it, and a tenant reads their own rows at `GET /t/<name>/audit` with their token. Nothing else is stored by the page. Behind a reverse proxy, start the server with `--trust-proxy` so those per-address limits key on `X-Forwarded-For` instead of on the proxy's own address, and only there, since the header is otherwise the client's to forge. `--public-url` and `--checkpoints-url` fill the sheet in. The witness closes the last gap: `agent-custody witness --key witness.key --log-url <url> --checkpoints-url <url> --out <dir> [--tenant <name>]...` runs on a machine the log's operator does not control, fetches each watched log's latest checkpoint, verifies it against the log's published keys, proves with the log's consistency proof that it extends the last head the witness signed, and countersigns it into `<dir>/<tenant>/<size>.json` and `latest.json`; a checkpoint that does not extend, or a second history at the same size, gets `ALARM.json` instead. Its key document is `<dir>/.well-known/agent-custody-witness.json`. Serve `<dir>` from the witness's own host; verifiers add `--witness-url` (or `--witness-key`) to `audit`, and the newer head must then carry the witness's signature. Two more things an operator needs. `agent-custody log-check --log-url <url> --checkpoints-url <url> [--witness-url <url>] [--tenant <name>]... [--max-lag <seconds>]` is the outside monitor: it verifies the head against the published keys, that the latest checkpoint verifies and keeps up with the head, that the head extends the checkpoint, and, with a witness, that the witness has countersigned, keeps up, and has raised no alarm; it exits 1 on any failure, so cron or a scheduled workflow on a machine that is not the log's turns it into an alert. `GET /health` on the server is the liveness check for a load balancer. And `GET /admin/usage?month=YYYY-MM`, on the admin page and as `/admin/usage.csv`, is the metering: appends per tenant for the month, leaves in total, live tokens, the numbers any invoice rests on. A tenant needs none of that to leave with their evidence: `agent-custody log-export --log-url <url> --tenant <name> --token-env AGENT_CUSTODY_LOG_TOKEN --out <dir>` fetches, with their own token, every leaf hash (`GET /t/<name>/leaves?since=&limit=`, pages of up to ten thousand), the signed head, the published keys, the signed checkpoints, their own usage (`GET /t/<name>/usage?month=`), and the administrative actions on their tenant (`GET /t/<name>/audit`, into `audit.json`), checks that the head and every checkpoint verify against the keys and that the leaves fetched hash to their roots, and writes `log.jsonl` in the format `verify --log` and `audit --log` read, so the export verifies receipts with no server at all. It exits 1 and says what did not add up if anything does not. Both routes answer only to that tenant's token. [deploy/](../../deploy/README.md) runs the server, the signer, Postgres, and the checkpoints host as containers, and [deploy/witness/](../../deploy/witness/) the witness.
110
+ Exactly one of the two. The bearer token comes from the named environment variable, never from the file, and a missing variable fails at startup. Add `"hashOnly": true` for any log run by someone else: the gateway then sends only the leaf hash, sha256 of the receipt envelope with the RFC 6962 prefix, so the log commits to the receipt without ever holding it, and the receipts with their arguments and results stay in `receiptsDir`. The verifier does not change; it hashes the envelope itself. A log that serves several tenants is reached at `<url>/t/<tenant>/`, and each of its tree heads names its log, which a verifier checks with `--log-id`. With a remote log the tree head in each receipt is signed by the log's key, and a verifier must be given that key with `--log-key`. An append that gets no answer within `timeoutMs` (default 10000) counts as unreachable and is retried like a server error, so a log that accepts connections and never answers cannot hold a call forever. If the log refuses a leaf, the receipt is not issued and the call returns an error to the agent. For an ordinary call the upstream action has already happened by then, and the error says so; a receipt that was never logged must not be handed out. For a tool named in `precommit` the order is reversed, below, and the action never happens. The reference log server is `node src/cli.ts log --file log.jsonl --key keys/log.key --port 8787 --token-env AGENT_CUSTODY_LOG_TOKEN [--log-id <id>] [--tenants tenants.json]`. It serves `POST /append` with `{leaf}` or `{leafHash}` (token required when one is configured), `GET /root?size=N`, `GET /consistency?old=M&new=N`, and `GET /head`; [verification.md](verification.md) says what each proves. `--log-id` writes that id into every tree head. `--tenants` names a JSON file, `{ "acme": { "file": "acme.jsonl", "tokenEnv": "ACME_TOKEN", "logId": "acme-eu" } }`, and each tenant is its own log at `/t/acme/…` with its own token and id; the default log stays at the root paths. With `--db-env DATABASE_URL` the server keeps its logs in Postgres instead of files, and needs the `pg` package beside it: leaves as hashes in one table keyed by tenant, one writer per tenant enforced with an advisory lock so a second instance is safe, tenants and their tokens in tables of their own with tokens stored only as hashes, and rate limits per token (50 appends a second, burst 100, a 64 KB body cap; a refused append answers 429 with `retry-after`, and the gateway's sink retries a few times). Tenants are managed with `log-admin --db-env DATABASE_URL`: `tenant add <id> [--log-id <id>]`, `token add <tenant> --label <text>` (the token is printed once), `token revoke <tenant> <hash-prefix>`, `tenant disable <id>`, `tenant plan <id> <free|team|enterprise>`, and `import --file log.jsonl [--tenant default]` to bring an existing file log in as hashes. Every tenant is on a plan, `free` unless moved: free allows ten thousand appends a calendar month, team a million, enterprise has no allowance. An append past the allowance is refused with 429, the numbers, and a `retry-after` that reaches the start of next month; the gateway behind it then withholds pre-committed calls, so a tenant out of quota never acts without evidence. The tenant's own `GET /t/<name>/usage` reports the plan and quota beside the month's appends. The tenant portal, `agent-custody portal --db-env DATABASE_URL --secret-env PORTAL_SECRET --public-url <log url> [--checkpoints-url <url>] [--portal-url <url>] [--stripe-key-env NAME --stripe-webhook-env NAME --stripe-price-team <price id>] [--trust-proxy]`, is the self-serve front of the same tables: a team registers with an email, a password (scrypt), and a tenant id and gets the tenant and its first key shown once with the welcome sheet; the dashboard shows appends against the plan, the tree size and root, the latest checkpoint, the log's URLs, keys, and the audit rows; keys are minted and revoked there, recorded as `portal:<email>`; the export command is on the page; and with the three Stripe variables the team plan is bought through Stripe Checkout, the signed webhook moving the plan (`stripe:<event>` in the audit trail) and the customer portal handling cancellation. Sessions are a signed cookie, `SameSite=Strict`, and every write needs a JSON body. The compose file runs it as the `portal` service at `PORTAL_HOST`. The root paths serve the tenant `default`, created on first start with `--log-id`, and `--token-env` still works for it. The key that signs tree heads can live in its own process: `agent-custody signer --key keys/log.key --port 8790 --token-env SIGNER_TOKEN` holds it and answers `POST /sign` with the shared secret and `GET /keys` to anyone; the log server then runs with `--signer-url http://signer:8790/ --signer-token-env SIGNER_TOKEN` instead of `--key`, and the process that faces the internet never holds the key. Either way the log serves its keys at `/.well-known/agent-custody-log.json`, current key first and retired keys (`--retired-key old.pub`) after it, so verifiers fetch and pin them with `verify --log-url` and `audit --log-url` rather than receiving a key file from the operator. With `--checkpoint-dir <dir>` the server publishes a signed checkpoint, every `--checkpoint-every` seconds (default 300), for each log whose tree has grown, and every `--checkpoint-heartbeat` seconds (default 21600, six hours) for a log that has not, so a quiet log's latest checkpoint is never more than six hours old and the monitor can tell quiet from stalled, as `<dir>/<tenant>/<treeSize>.json` and `latest.json`, and with a database also as rows; `GET /checkpoints?since=<size>` and `GET /t/<tenant>/checkpoints` list them. Serve the directory read-only from a second host, so the record of what the log signed does not depend on the log's API being up; a verifier who kept an earlier head audits against a later checkpoint with `audit --older <bundle> --newer <checkpoint> --log-url <url>`. With `--admin-token-env ADMIN_TOKEN` (Postgres only) the server also serves the operator's page at `/admin` and its API under `/admin/`: list and create tenants, mint a token that is shown once beside the tenant's welcome sheet, revoke tokens, disable tenants. Everything under `/admin`, the page included, needs the admin token: the browser asks for it (any user name, the token as the password) and an API client sends it as a bearer; a handful of wrong attempts from one address are throttled for a minute. Every change made there or with `log-admin` is recorded: who (the name entered at the browser prompt and the address, `bearer` for an API client, or the user and host for the command line), what (`tenant.add`, `tenant.disable`, `token.add`, `token.revoke`), which tenant, and the detail, never the token itself; the page shows it under Activity, `GET /admin/audit?tenant=&limit=` and `log-admin audit` list it, and a tenant reads their own rows at `GET /t/<name>/audit` with their token. Nothing else is stored by the page. Behind a reverse proxy, start the server with `--trust-proxy` so those per-address limits key on `X-Forwarded-For` instead of on the proxy's own address, and only there, since the header is otherwise the client's to forge. `--public-url` and `--checkpoints-url` fill the sheet in. The witness closes the last gap: `agent-custody witness --key witness.key --log-url <url> --checkpoints-url <url> --out <dir> [--tenant <name>]...` runs on a machine the log's operator does not control, fetches each watched log's latest checkpoint, verifies it against the log's published keys, proves with the log's consistency proof that it extends the last head the witness signed, and countersigns it into `<dir>/<tenant>/<size>.json` and `latest.json`; a checkpoint that does not extend, or a second history at the same size, gets `ALARM.json` instead. Its key document is `<dir>/.well-known/agent-custody-witness.json`. Serve `<dir>` from the witness's own host; verifiers add `--witness-url` (or `--witness-key`) to `audit`, and the newer head must then carry the witness's signature. Two more things an operator needs. `agent-custody log-check --log-url <url> --checkpoints-url <url> [--witness-url <url>] [--tenant <name>]... [--max-lag <seconds>]` is the outside monitor: it verifies the head against the published keys, that the latest checkpoint verifies and keeps up with the head, that the head extends the checkpoint, and, with a witness, that the witness has countersigned, keeps up, and has raised no alarm; it exits 1 on any failure, so cron or a scheduled workflow on a machine that is not the log's turns it into an alert. `GET /health` on the server is the liveness check for a load balancer. And `GET /admin/usage?month=YYYY-MM`, on the admin page and as `/admin/usage.csv`, is the metering: appends per tenant for the month, leaves in total, live tokens, the numbers any invoice rests on. A tenant needs none of that to leave with their evidence: `agent-custody log-export --log-url <url> --tenant <name> --token-env AGENT_CUSTODY_LOG_TOKEN --out <dir>` fetches, with their own token, every leaf hash (`GET /t/<name>/leaves?since=&limit=`, pages of up to ten thousand), the signed head, the published keys, the signed checkpoints, their own usage (`GET /t/<name>/usage?month=`), and the administrative actions on their tenant (`GET /t/<name>/audit`, into `audit.json`), checks that the head and every checkpoint verify against the keys and that the leaves fetched hash to their roots, and writes `log.jsonl` in the format `verify --log` and `audit --log` read, so the export verifies receipts with no server at all. It exits 1 and says what did not add up if anything does not. Both routes answer only to that tenant's token. [deploy/](../../deploy/README.md) runs the server, the signer, Postgres, and the checkpoints host as containers, and [deploy/witness/](../../deploy/witness/) the witness.
109
111
 
110
112
  `otel`, optional in both the gateway and SDK configs, sends every receipt to the collector you already run as one span over OTLP/HTTP, after the receipt is issued: `"otel": { "url": "http://localhost:4318", "headersEnv": { "x-api-key": "OTEL_KEY" }, "serviceName": "support-agents" }`. The span's trace id is the receipt id, its attributes carry the tool, agent, principal, execution status, policy decision, and log position, and its status is an error only when the upstream failed or errored, since a denial is the policy working. Export is best effort: a collector that is down or refuses costs a line on stderr, never a receipt. Tutorial 18 shows it against a stand-in collector.
111
113
 
@@ -123,6 +125,10 @@ node src/cli.ts gateway --config ./gateway.json
123
125
 
124
126
  You will not normally run this by hand. The agent host spawns it, as below.
125
127
 
128
+ ## One gateway for many agents, over HTTP
129
+
130
+ `agent-custody gateway --config gateway.json --http --port 8790` serves the same gateway as an MCP server over Streamable HTTP at `/mcp`, and every connection presents its own grant: the delegation envelope, base64url-encoded, as `Authorization: Bearer <value>` or `X-Agent-Custody-Grant` on the initialize request. The gateway verifies it against `trustedPrincipalKeys` and its validity window, and opens a session for exactly that grant; a grant signed by a stranger, an expired one, or none at all gets 403 with the reason and no session (403 rather than 401, because MCP clients treat 401 as an OAuth challenge and hide the body). Sessions share the key, the policy, the upstreams, the log, and the fact lookups; each has its own tools (the scopes its grant names), its own receipts (its own principal and agent, attested), and its own consumed facts. `grantFile` in the config is then optional and ignored. A session ends when the client terminates it or after `--idle-minutes` (default 30) without a request; `GET /health` reports the live count and the gateway's key id. The transport is plain HTTP: bind to loopback, a private network, or put TLS in front. From JavaScript, `grantHeader(envelope)` builds the header value; from any language it is `base64url(JSON.stringify(envelope))`. Tutorial 20 runs two agents against one gateway.
131
+
126
132
  ## Wiring it into an agent host
127
133
 
128
134
  The gateway is an ordinary MCP server, so any host that can launch a stdio MCP server can use it. Point the host at the gateway instead of at the upstream server.
@@ -70,6 +70,7 @@ execution observed executed
70
70
  | gateway receipt carries a delegation | gateway receipts always embed the signed grant they enforced | an SDK receipt relabelled as gateway |
71
71
  | gateway receipt carries a policy decision | gateway receipts always record the Cedar decision | same |
72
72
  | delegation signature (principal key) | the embedded grant was signed by a key you trust as a principal | a grant the principal never issued |
73
+ | delegation chain to the principal | only when the grant embeds a parent: every link is signed by the key its parent names, holds no scope its parent lacks, sits inside its parent's window, and names the same principal, up to three delegations deep | a sub-agent given more than its delegator had, a link signed by the wrong key, a chain that never reaches a trusted principal |
73
74
  | delegation binds principal and agent | the grant names the same principal and agent the receipt names, and the principal keyid matches | a valid grant for someone else, spliced in |
74
75
  | delegation valid at receipt time | the receipt's timestamp is inside the grant's window | expired or not-yet-valid authority |
75
76
  | executed tool within delegated scope | if the tool ran, the grant covered it. Denied calls pass this check by construction | a gateway that forwarded out of scope |
@@ -88,7 +89,7 @@ execution observed executed
88
89
  | log inclusion proof | this exact envelope is a leaf of the tree with that root | receipt never logged, or logged then changed |
89
90
  | log file root matches tree head | recomputing the root from your copy of the log at that size gives the same value | your log copy and the issuer's history diverge: deletion, reordering, or edit |
90
91
 
91
- Gateway receipts run seventeen checks, eighteen with a log file, and five more when the tool was committed before it ran (`precommit` in the gateway config). SDK receipts run fewer, because there is no delegation to check, and the report says so on the `principal is claimed` line.
92
+ Gateway receipts run seventeen checks, eighteen with a log file, and five more when the tool was committed before it ran (`precommit` in the gateway config). SDK receipts run fewer, because there is no delegation to check, and the report says so on the `principal is claimed` line. A receipt issued to a sub-agent under a delegation chain runs one more, `delegation chain to the principal`.
92
93
 
93
94
  ## What a verified receipt lets you conclude
94
95
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-custody/receipts",
3
- "version": "0.5.9",
3
+ "version": "0.6.1",
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": {
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@langchain/core": "^1.2.9",
70
- "@openai/agents": "^0.17.0",
70
+ "@openai/agents": "^0.18.0",
71
71
  "@types/node": "^26.4.1",
72
72
  "ai": "^7.0.92",
73
73
  "tsx": "^4.23.13",
@@ -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": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODViYjZhMjk1YzYxYmIzMzg5MWYyNTFiNWNiZGQ4NzNjNGVkYTU5OTBkMWE4MzVjNDRjYjZiMDgwNTBhY2U2MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjkzWiIsInRyZWVTaXplIjoxfQ==",
10
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiNjk4ZjdhMDg4ZWQ3ZGMzNDhiZmNkNDBmZDhhZDUxOGQ3YzMxMjc5MTFhMDYzZjgzOTYxOGY5ZTIzNzg3OTkwMCIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTE2WiIsInRyZWVTaXplIjoxfQ==",
11
11
  "signatures": [
12
12
  {
13
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
14
- "sig": "xhkG/l7l3ElnvGnxrFfK2nYUCkRo0ktj0+6ctv6V1qKiXihMYexcOu/qepdqWWN0mWFq2u8/Aqo6kbdoWhiFDQ=="
13
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
14
+ "sig": "tgXaLpauD+TMD8l9+qZCk4dVOlp+bhlwzNYQ/LBN3o9cjQSpeUH2Z1Qriog4jHz3HndJ3eSheBmumin04XrPAw=="
15
15
  }
16
16
  ]
17
17
  },
18
18
  "newer": {
19
19
  "payloadType": "application/vnd.agent-custody.treehead+json",
20
- "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODY0N2E0MTczNTIxYzE5Mzg4NTg2Y2U1MzYyNGU3MGZiYWEyMzc0NGRiYjIzNTQ0ZDU2N2Q5NDY0NmE0ZTM3MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjk3WiIsInRyZWVTaXplIjoyfQ==",
20
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiZTk3YzI5MjBmYTM4MGJiNTVhZjI4ODczZGNhNmY4NzM0ZGFiZTNmYjNmYjZlOWEwYmQ5YTgwNTVkNjJkYzRmYyIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTIxWiIsInRyZWVTaXplIjoyfQ==",
21
21
  "signatures": [
22
22
  {
23
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
24
- "sig": "iaweKtCddKNA0R+dkq+0qyzwe8RhZdKgK6y9o21adUCjhMAGlpl+YDe1chzn4NvT7jIZvn+HDdll136TmqcmCQ=="
23
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
24
+ "sig": "D1sCDTur/vV9Agzys9RNyRQjC/z4xJU+Ch1rWRksCx8ISP54+EEL9PlDEKZrAigX6hSim1zai42ssgaR+SdGBQ=="
25
25
  }
26
26
  ]
27
27
  },
28
28
  "proof": [
29
- "f8e988293efa93dd5e5ea2af96b88588f4a1fba5c786848e997aa985c95e2f81"
29
+ "00be3e432c1d9e0b24bdd55891e75611b4cc4da06736c2805b27bae2c1e39578"
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": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODY0N2E0MTczNTIxYzE5Mzg4NTg2Y2U1MzYyNGU3MGZiYWEyMzc0NGRiYjIzNTQ0ZDU2N2Q5NDY0NmE0ZTM3MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjk3WiIsInRyZWVTaXplIjoyfQ==",
44
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiZTk3YzI5MjBmYTM4MGJiNTVhZjI4ODczZGNhNmY4NzM0ZGFiZTNmYjNmYjZlOWEwYmQ5YTgwNTVkNjJkYzRmYyIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTIxWiIsInRyZWVTaXplIjoyfQ==",
45
45
  "signatures": [
46
46
  {
47
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
48
- "sig": "iaweKtCddKNA0R+dkq+0qyzwe8RhZdKgK6y9o21adUCjhMAGlpl+YDe1chzn4NvT7jIZvn+HDdll136TmqcmCQ=="
47
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
48
+ "sig": "D1sCDTur/vV9Agzys9RNyRQjC/z4xJU+Ch1rWRksCx8ISP54+EEL9PlDEKZrAigX6hSim1zai42ssgaR+SdGBQ=="
49
49
  }
50
50
  ]
51
51
  },
52
52
  "newer": {
53
53
  "payloadType": "application/vnd.agent-custody.treehead+json",
54
- "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODViYjZhMjk1YzYxYmIzMzg5MWYyNTFiNWNiZGQ4NzNjNGVkYTU5OTBkMWE4MzVjNDRjYjZiMDgwNTBhY2U2MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjkzWiIsInRyZWVTaXplIjoxfQ==",
54
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiNjk4ZjdhMDg4ZWQ3ZGMzNDhiZmNkNDBmZDhhZDUxOGQ3YzMxMjc5MTFhMDYzZjgzOTYxOGY5ZTIzNzg3OTkwMCIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTE2WiIsInRyZWVTaXplIjoxfQ==",
55
55
  "signatures": [
56
56
  {
57
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
58
- "sig": "xhkG/l7l3ElnvGnxrFfK2nYUCkRo0ktj0+6ctv6V1qKiXihMYexcOu/qepdqWWN0mWFq2u8/Aqo6kbdoWhiFDQ=="
57
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
58
+ "sig": "tgXaLpauD+TMD8l9+qZCk4dVOlp+bhlwzNYQ/LBN3o9cjQSpeUH2Z1Qriog4jHz3HndJ3eSheBmumin04XrPAw=="
59
59
  }
60
60
  ]
61
61
  },
62
62
  "proof": [
63
- "f8e988293efa93dd5e5ea2af96b88588f4a1fba5c786848e997aa985c95e2f81"
63
+ "00be3e432c1d9e0b24bdd55891e75611b4cc4da06736c2805b27bae2c1e39578"
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": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODViYjZhMjk1YzYxYmIzMzg5MWYyNTFiNWNiZGQ4NzNjNGVkYTU5OTBkMWE4MzVjNDRjYjZiMDgwNTBhY2U2MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjkzWiIsInRyZWVTaXplIjoxfQ==",
80
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiNjk4ZjdhMDg4ZWQ3ZGMzNDhiZmNkNDBmZDhhZDUxOGQ3YzMxMjc5MTFhMDYzZjgzOTYxOGY5ZTIzNzg3OTkwMCIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTE2WiIsInRyZWVTaXplIjoxfQ==",
81
81
  "signatures": [
82
82
  {
83
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
84
- "sig": "xhkG/l7l3ElnvGnxrFfK2nYUCkRo0ktj0+6ctv6V1qKiXihMYexcOu/qepdqWWN0mWFq2u8/Aqo6kbdoWhiFDQ=="
83
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
84
+ "sig": "tgXaLpauD+TMD8l9+qZCk4dVOlp+bhlwzNYQ/LBN3o9cjQSpeUH2Z1Qriog4jHz3HndJ3eSheBmumin04XrPAw=="
85
85
  }
86
86
  ]
87
87
  },
88
88
  "newer": {
89
89
  "payloadType": "application/vnd.agent-custody.treehead+json",
90
- "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODY0N2E0MTczNTIxYzE5Mzg4NTg2Y2U1MzYyNGU3MGZiYWEyMzc0NGRiYjIzNTQ0ZDU2N2Q5NDY0NmE0ZTM3MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjk3WiIsInRyZWVTaXplIjoyfQ==",
90
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiZTk3YzI5MjBmYTM4MGJiNTVhZjI4ODczZGNhNmY4NzM0ZGFiZTNmYjNmYjZlOWEwYmQ5YTgwNTVkNjJkYzRmYyIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTIxWiIsInRyZWVTaXplIjoyfQ==",
91
91
  "signatures": [
92
92
  {
93
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
94
- "sig": "iaweKtCddKNA0R+dkq+0qyzwe8RhZdKgK6y9o21adUCjhMAGlpl+YDe1chzn4NvT7jIZvn+HDdll136TmqcmCQ=="
93
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
94
+ "sig": "D1sCDTur/vV9Agzys9RNyRQjC/z4xJU+Ch1rWRksCx8ISP54+EEL9PlDEKZrAigX6hSim1zai42ssgaR+SdGBQ=="
95
95
  }
96
96
  ]
97
97
  },
98
98
  "proof": [
99
- "f8e988293efa93dd5e5ea2af96b88588f4a1fba5c786848e997aa985c95e2f81"
99
+ "00be3e432c1d9e0b24bdd55891e75611b4cc4da06736c2805b27bae2c1e39578"
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": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODViYjZhMjk1YzYxYmIzMzg5MWYyNTFiNWNiZGQ4NzNjNGVkYTU5OTBkMWE4MzVjNDRjYjZiMDgwNTBhY2U2MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjkzWiIsInRyZWVTaXplIjoxfQ==",
117
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiNjk4ZjdhMDg4ZWQ3ZGMzNDhiZmNkNDBmZDhhZDUxOGQ3YzMxMjc5MTFhMDYzZjgzOTYxOGY5ZTIzNzg3OTkwMCIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTE2WiIsInRyZWVTaXplIjoxfQ==",
118
118
  "signatures": [
119
119
  {
120
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
121
- "sig": "xhkG/l7l3ElnvGnxrFfK2nYUCkRo0ktj0+6ctv6V1qKiXihMYexcOu/qepdqWWN0mWFq2u8/Aqo6kbdoWhiFDQ=="
120
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
121
+ "sig": "tgXaLpauD+TMD8l9+qZCk4dVOlp+bhlwzNYQ/LBN3o9cjQSpeUH2Z1Qriog4jHz3HndJ3eSheBmumin04XrPAw=="
122
122
  }
123
123
  ]
124
124
  },
125
125
  "newer": {
126
126
  "payloadType": "application/vnd.agent-custody.treehead+json",
127
- "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiODY0N2E0MTczNTIxYzE5Mzg4NTg2Y2U1MzYyNGU3MGZiYWEyMzc0NGRiYjIzNTQ0ZDU2N2Q5NDY0NmE0ZTM3MiIsInRpbWVzdGFtcCI6IjIwMjYtMDktMDhUMDY6MzA6MDQuNjk3WiIsInRyZWVTaXplIjoyfQ==",
127
+ "payload": "eyJsb2ciOiJ2ZWN0b3JzLWxvZyIsInJvb3RIYXNoIjoiZTk3YzI5MjBmYTM4MGJiNTVhZjI4ODczZGNhNmY4NzM0ZGFiZTNmYjNmYjZlOWEwYmQ5YTgwNTVkNjJkYzRmYyIsInRpbWVzdGFtcCI6IjIwMjYtMDktMjFUMDk6Mjc6MzIuMTIxWiIsInRyZWVTaXplIjoyfQ==",
128
128
  "signatures": [
129
129
  {
130
- "keyid": "74356bcffe957c1c3a3bde6702505f536d4e8bab2aac3b0f36419e846da1a8d5",
131
- "sig": "iaweKtCddKNA0R+dkq+0qyzwe8RhZdKgK6y9o21adUCjhMAGlpl+YDe1chzn4NvT7jIZvn+HDdll136TmqcmCQ=="
130
+ "keyid": "bb208e67e605dc54aad7c6d6d05cbe6e9531f4d6ef70edd50729f82199fcc5d1",
131
+ "sig": "D1sCDTur/vV9Agzys9RNyRQjC/z4xJU+Ch1rWRksCx8ISP54+EEL9PlDEKZrAigX6hSim1zai42ssgaR+SdGBQ=="
132
132
  }
133
133
  ]
134
134
  },
@@ -53,18 +53,18 @@
53
53
  }
54
54
  ],
55
55
  "keyid": {
56
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA0ZCk/vXbGqi5xjdFAok3XGPWbCT9yO2OeGbMF1nvH5w=\n-----END PUBLIC KEY-----\n",
57
- "keyid": "3df0af0ec3abda622a30f7a3273fc6ca4eda229404122f02e2cae12923a861ad"
56
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA9PR1NM7CAYV6ztqdIxlV8EtbwKt/noC1QE1/uu64w8A=\n-----END PUBLIC KEY-----\n",
57
+ "keyid": "4b54e3508693bbc3e6236cd42834826ba45205a4e4d3b14a645e12e0d4cc0901"
58
58
  },
59
59
  "dsse": {
60
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA0ZCk/vXbGqi5xjdFAok3XGPWbCT9yO2OeGbMF1nvH5w=\n-----END PUBLIC KEY-----\n",
60
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA9PR1NM7CAYV6ztqdIxlV8EtbwKt/noC1QE1/uu64w8A=\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": "3df0af0ec3abda622a30f7a3273fc6ca4eda229404122f02e2cae12923a861ad",
67
- "sig": "aGI6lAPRJs17z+UUezv/7iGyZ1rHbjKRWBELgeC4YjLGytQLUZsH+2jwFKhmTasJIyssry1ob+HMt4V20WCzBQ=="
66
+ "keyid": "4b54e3508693bbc3e6236cd42834826ba45205a4e4d3b14a645e12e0d4cc0901",
67
+ "sig": "/xY/5ueLIFwQYepoDm4sOGtxO9MntKEbYV86DwPlcosuIbcc92J2SdItEeSQdE54v5N/LTm2iqk6jMCl1rLVAw=="
68
68
  }
69
69
  ]
70
70
  },