Walkthrough: agent audit trail
A pipe that records what went through it, while your agent keeps talking to the system it was talking to. This page puts one capture URL in front of one destination. Afterwards the record answers, without asking the agent: what was sent, who signed it, when, and what the destination said back. Every prompt below was run on 2026-09-08 against flurryport 0.6.12, and the answers are what came back.
It assumes the connector is installed and holds a write token, which is steps 1 to 5 of the connector walkthrough. The destination here runs on localhost so anyone can follow along; a destination on the internet works the same and needs no listener. When one destination becomes several, the fan-out and routing walkthroughs pick up from here.
1. One capture URL, signed
The first decision is where the agent’s actions pass through. This step creates one capture URL and turns on signature checking, so only posts signed with this endpoint’s key are accepted, and an unsigned one is refused before anything is stored. From here on, the agent posts to this URL instead of straight to the destination, and that is the whole change on the agent’s side.
create_endpoint and set_endpoint_signing, then get_capture_url for the address:captureUrl: https://api.flurryport.io/api/v1/capture/<project>/agent-trail signing: live; unsigned posts bounce 401 keyRef: signing:2o2A50Y7mZj3WmnUxLmi6S
2. The destination, and a listener for it
Next, tell the pipe where things go. This step adds the one system the agent was already using and arms it, so every capture is delivered to it automatically. Because this destination is on your machine, a listener carries the delivery the last hop and records the response; a destination on the internet needs no listener.
create_replay_target with auto-forward on:Warehouse 3miOeJxfePyVMsx9bsf3yi autoReplay: true class: Local Target is ARMED (autoReplay on): captures fan out to it automatically.
flurryport listen http://localhost:8765/hookWhat it prints once the action in the next step arrives:
Listening on http://localhost:8765/hook flurryport-operations/agent-trail → Warehouse Polling every 2000ms. Press Ctrl+C to stop. 3:49:23 PM ← 3:49:21 PM POST → 200 OK (3ms)
flurryport echo http://localhost:8765/hookIt answers 200, logs every request, and mirrors the headers and body back, so the record shows the round trip. Your assistant can start one too ("Start an echo server on port 8765." runs
start_echo_server inside the connector process), but then only the record shows you what arrived. Prove the pipe against the echo, then swap in the real service.3. The agent acts
Now the agent does its job the way it always did, except that it posts to the capture URL. This step sends one typed action and shows the receipt: the post was signature-checked and stored, and one delivery was queued. The receipt is what the agent can show its human at once; the delivery is what the record fills in over the next seconds.
post_intent with this body, signed on the way out in a header you never type:{ "v": 1, "kind": "message", "from": "agent", "to": "all",
"verb": "fp:act", "summary": "Ship order 1045 to the warehouse",
"args": { "order": "1045", "action": "ship" } }The receipt comes back at once:status: accepted
signedWith: { keyRef: "signing:2o2A50Y7mZj3WmnUxLmi6S", scheme: "simple" }
captureId: 5sbojdjQQNqpWrKMAqJDrq
postedAt: 2026-09-08T21:49:22.578Z
executions: 6UMGxubrnd9ZBuqoNzX56D -> Warehouse4. What the record holds
This is the audit trail. This step reads it back three ways: the delivery with its status and timing, what the destination actually received, and the capture itself with the label of the key that signed it. None of it comes from the agent; all of it was written by the pipe as the action passed through.
get_capture_executions returns the row:Warehouse Completed 200 3 ms created 21:49:21.42 completed 21:49:22.90
X-Flurry-Signature: 2cdcabe111a2a6cb…5f7dc2
{ "v": 1, "kind": "message", "from": "agent", "to": "all", "verb": "fp:act",
"summary": "Ship order 1045 to the warehouse", "args": { "order": "1045", "action": "ship" } }get_capture returns the same body and, beside it, what the server fixed when it landed:body.from: agent (what the sender wrote) MatchedSignerLabel: owner (the key that signed, matched by the server) MatchedSigningRef: FP_SIGNING_985e…3a5c CreatedAt: 2026-09-08T21:49:21.40The
from field says agent because that is what the sender wrote in it. The signer label beside it is the key that actually signed, matched by the server at capture time. Attribution lives there, not in the message.5. Check it later, and decide how long it lasts
An audit trail is only useful when you come back to it. This step is the same reads months on, plus the two decisions that are cheaper before you need the history: the retention window and who may read the record without writing to it.
get_capture and get_capture_executions. The signer label and the timestamps were fixed when the post landed, and the order is the server’s, not the sender’s.What this does not establish
- Not that a human approved the action. Signed does not mean human-approved.
- Not that the destination told the truth about what it did with the delivery.
- Not why the agent decided anything.
- Nothing the agent sent somewhere other than the capture URL. The pipe records what went through it.
- Nothing outside the window you chose. A trail is worth what it outlives.
See also
- Agent audit fan-out: the same action to every destination.
- Agent audit routing: each action to the destination its rule names.
- Auto-forward and targets: arming a destination and what a delivery record holds.
- Agent audit trail, agent action receipt, and action provenance: the ideas behind the steps.