case studies

Replaying an AI Agent Publish Session With the Tokening Flight Recorder

An agent was given one job: publish post 7, the launch note someone had already approved. It got a grant for exactly that. One call to publish_post, bound to id 7, valid for 15 minutes. Afterwards we wanted to know what it actually did, in order, without asking the agent.

This is a real run, recorded on Tokening 0.1.4. The upstream was a small stand-in CMS that speaks MCP over HTTP, running on the same machine, so nothing touched a real site. The script, the policy and every line of output are in the case study folder of the code repo, and you can run it again yourself.

What the agent did

It asked for its tool list first and got one tool back, publish_post. Then it tried to publish post 8. Then post 7. Then post 7 again.

The agent saw three answers. Post 8 was refused with “Grant is bound to id 7; call sent id 8.” Post 7 came back as “post 7 saved”. The repeat was refused with “allowed 1 use and has none left.” The CMS log agrees: from this agent it received one call, publish_post on id 7.

What the replay shows

$ tokening flight replay --db tokening.db --agent publisher-bot
#1  2026-10-31T10:40:12.648Z  grant     g_e7f2d5d81610f2790640cc31683ca017 issued to publisher-bot by ops-maria: cms.publish-post on publish_post, 1 use, expires 2026-10-31T10:55:12.648Z, bound id, reason "publish the approved launch note"
#2  2026-10-31T10:40:12.667Z  request   g_e7f2d5d81610f2790640cc31683ca017 publisher-bot asked for publish_post on cms with arguments id (digest 644da283c2c9)
#3  2026-10-31T10:40:12.667Z  decision  g_e7f2d5d81610f2790640cc31683ca017 refused (arg_mismatch): Grant is bound to id 7; call sent id 8. 1 use left.
#4  2026-10-31T10:40:12.671Z  request   g_e7f2d5d81610f2790640cc31683ca017 publisher-bot asked for publish_post on cms with arguments id (digest 14fb46f66868)
#5  2026-10-31T10:40:12.671Z  decision  g_e7f2d5d81610f2790640cc31683ca017 allowed: Call fits this grant. One use was consumed before forwarding. 0 uses left.
#6  2026-10-31T10:40:12.685Z  result    g_e7f2d5d81610f2790640cc31683ca017 upstream answered HTTP 200 in 10 ms
#7  2026-10-31T10:40:12.688Z  request   g_e7f2d5d81610f2790640cc31683ca017 publisher-bot asked for publish_post on cms with arguments id (digest 14fb46f66868)
#8  2026-10-31T10:40:12.688Z  decision  g_e7f2d5d81610f2790640cc31683ca017 refused (grant_exhausted): Grant g_e7f2d5d81610f2790640cc31683ca017 allowed 1 use and has none left. 0 uses left.
Chain intact through entry 20.

Eight entries for this agent, out of 20 in the whole log. Other agents worked in the same database during the run, and the filter left them out. The last line still covers all 20, because replay checks the full chain before it prints anything.

A few things are worth reading closely.

The tool list call isn’t there. Listing tools spends nothing and sends nothing upstream, so it isn’t recorded. The log holds calls that could change something.

The refused attempt on post 8 is in the log, with its reason, even though it never left Tokening. If the agent had been steered by a prompt injection into reaching for a different post, this is where you’d see it.

Entries 4 and 7 have the same digest, 14fb46f66868. Same tool, same arguments, same values. The second call was a plain retry of the first, and the log can show that without storing the value. The digest is a SHA-256 of the whole call. The entry itself keeps argument names only.

There’s exactly one result entry, HTTP 200 in 10 ms. One use, one call out the door.

Checking it while the server runs

The replay and a full check both ran with tokening serve still up. The flight commands open the database read-only.

$ tokening flight verify --db tokening.db
Intact: 20 entries, 1 to 20.
Head: 20:adff53dd54fbae58a0df9c57fa306b995135b864d228365fc9bd2ca2511fc276

That last line is worth copying somewhere else. The case study on edited log rows shows why.

What this run doesn’t show

The log covers what passed through Tokening. If the agent had a second way into the CMS, say a password left in its environment, nothing here would show it. That’s the reason the real key lives only in Tokening’s vault.

The flight recorder post explains the format, and the recipes post has the commands for day-to-day use.