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.

1
"Create an endpoint called Agent trail in my project and turn on signing." The assistant runs 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.

1
"Add a destination called Warehouse at http://localhost:8765/hook and forward every capture to it." create_replay_target with auto-forward on:
Warehouse  3miOeJxfePyVMsx9bsf3yi  autoReplay: true  class: Local
Target is ARMED (autoReplay on): captures fan out to it automatically.
2
In a terminal, attach the listener to that address (flurryport 0.6.12 or newer takes the address; older versions offer a menu). It polls for deliveries queued to that target, forwards each one to your localhost service with the body and the signature header intact, prints what it forwarded and what came back, and records the response into the record:
flurryport listen http://localhost:8765/hook
What 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)
3
No warehouse service yet? Run an echo server yourself, in a terminal, so you can watch what lands:
flurryport echo http://localhost:8765/hook
It 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.

1
"Post to Agent trail: ship order 1045 to the warehouse." The assistant runs 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 -> Warehouse

4. 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.

1
"Show me the delivery for that capture." get_capture_executions returns the row:
Warehouse  Completed  200  3 ms   created 21:49:21.42  completed 21:49:22.90
2
"What did the warehouse receive?" The body as posted, carrying the endpoint’s signature header so the destination can check it too:
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" } }
3
"Show me that capture and who signed it." 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.40
The 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.

1
Read the same capture and delivery again with 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.
2
Captures are kept for your plan’s window, and the window is on the plans and limits page. A question you will ask in three weeks needs a window longer than three weeks. Reading does not need write access: an invite with the monitor role reads the record and cannot post, and on that scoped credential personal data in what is read back is masked.

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