All lessons › Function B — Securing AI Architectures › CyberTravels' Architecture, and Every Risk It Carries
B1.14

Repudiation and untraceability

B1
▶
Lightboard recording

Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.

Video placeholderpublishes automatically from recordings/B1.14.mp4
Risk

You cannot say which user caused an action, or what made the agent decide — so the incident cannot be scoped and the action cannot be attributed.

Control

Attribution carried on every hop, in a store the agent cannot write to (B2.7).

The lab

Reconstruct who caused a deletion from a log that records only tool calls.

◉Use case relevance

The trace shows the tool call. It does not show the text that motivated the call, or the human the agent was acting for. Six weeks later, nobody can say whether that action was authorised — including the person who authorised it.

At CyberTravels. cybertravels/db.py's audit table, as A2.2 left it. Every row names the human and the agent. None of them names the vendor notice the agent read eleven seconds earlier, which is the fourth question and the one B2.7 closes.

◴What this lesson is — Day 0, Day 1, Day 2

What it covers. Reconstruct who caused a deletion from a log that records only tool calls.

Why a security engineer needs it. You cannot say which user caused an action, or what made the agent decide — so the incident cannot be scoped and the action cannot be attributed. The control it builds is: attribution carried on every hop, in a store the agent cannot write to (B2.7).

This is a risk lesson: it shows the failure happening before anything tries to stop it, so the control that follows is answering something you have already watched go wrong.

Day 0 — why

You cannot say which user caused an action, or what made the agent decide, so the incident cannot be scoped.

Day 1 — how

Attribution carried on every hop, into a store the agent cannot write to.

Day 2 — measure

Actions whose full chain — human, agent, scope — can be reconstructed. Anything below 1.00 is what you cannot audit.

Measure this

what to measuredenominator or target
actions whose full chain — human, agent, scope — can be reconstructedshare; anything below 1.00 is what you cannot audit

▦The framework, and how it works

   what the trace records          what the question needs
   +----------------------+        +---------------------------+
   | tool: delete_branch  |        | which human asked?         |
   | at: 03:14:22         |        | which agent acted?         |
   | result: ok           |        | under what authority?      |
   +----------------------+        | what input motivated it?   |
                                   +---------------------------+

   complete logs, unanswerable question

OWASP T8 — Repudiation & Untraceability.

The observability component decides whether anything that just happened can be explained. Most agent logging records tool calls: which tool, what arguments, what came back. That is enough to debug the agent and not enough to investigate it.

Three fields are usually missing, and each one removes a different question from the set you can answer.

The human principal. Without it, "which user caused this?" has no answer — the log says agent-svc, as in B1.6.

The motivating input. The tool call is recorded; the thing that made the agent decide to call it is not. So root cause cannot be established at all. You can see that the agent emailed a file, and nothing tells you the retrieved document that told it to.

The delegation chain. In a multi-agent topology, which hop originated this? Without the chain you have a set of actions and no order.

There is a fourth problem that is structural rather than about fields: if the agent can write to the log store, the log is not evidence. An agent with credentials broad enough to be interesting usually has credentials broad enough to touch the observability stack, and nobody notices until they need the record to be trustworthy.

Where this lands on the reference architecture. `` ingress -> orchestrator -> agent_runtime -> model | | messaging tools / mcp | | knowledge / memory egress identity + policy wrap every call · observability records it ``

2 · The risk, realised

A deletion happened. Answer three questions from the log you have.

3 · The check, as a skill

CyberTravels logs every tool call. The skill puts the three questions an investigation opens with to one real record, and reports per question the field that would have answered it.

▶Real time execution as skill

The skill — skills/threats/audit-answerability-check/SKILL.md

name: audit-answerability-check
description: >-
  Put an investigation's three questions to an existing agent log — which human,
  what motivated the action, which hop originated it — and record which of them
  the record cannot answer. Use when reviewing agent telemetry before an
  incident rather than during one.
allowed-tools: Read, Grep, Glob

A complete log that answers nothing

Agent logs are usually complete in the sense that every tool call is present. They are useless in the sense that the three questions an investigation opens with have no fields behind them. The check is not "are we logging" — it is "which question dies here".

When to use this

Before an incident. Run it against a real log line from production, not against the logging design.

Procedure

1 — Take one real record. A single tool-call row, with every field it actually carries. Not the schema — the row.

2 — Ask: which human? Is there a field naming the principal on whose behalf this ran? An agent identity is not an answer; it is the thing that ran.

3 — Ask: what motivated it? Which input caused this call — the user's request, a retrieved document, a tool result, a memory record. Without it you cannot tell an instructed action from an injected one, and that is the distinction the whole investigation turns on.

4 — Ask: which hop originated it? In any multi-agent or delegated flow, which agent started the chain. A chain reconstructed by correlating timestamps across four services is a chain you will not reconstruct at 2am.

5 — Report per question, with the field that would answer it. Three rows. Each says: answerable yes/no, and the field to add. Anything vaguer produces a logging project rather than a fix.

Example

Input — the fixture committed at the top of scripts/audit_answerability_check.py. Edit it and re-run: the buckets, counts and verdicts below are derived from it, not hard-coded.

Output — the opening lines of a real run:

the log you have:
   09:14:02  agent-svc search      {'q': 'invoice 8812'}
   09:14:07  agent-svc fetch_doc   {'id': 'wiki/473'}
   09:14:11  agent-svc run_query   {'sql': 'DELETE FROM invoices WHERE id=8812'}
   09:14:12  agent-svc send_email  {'to': 'ops@corp.example'}

question                                    field needed        present?
which user caused the deletion?             principal           NO

The run continues past this. test_skills.py executes the script on every build, with no model configured — so what CI proves is that it runs and refuses legibly, not that it prints these lines. The output above is a recorded run against a real model, and nothing re-diffs it: a model's answer is not reproducible, and this repository does not claim otherwise anywhere it can be checked.

Output contract

{
  "record": {"fields": ["str"]},
  "questions": [{"question": "which human|what motivated|which hop", "answerable": false, "missing_field": "str"}],
  "answerable_count": 0,
  "reconstruction": {"possible": false, "requires_correlating": ["str"]}
}

Failure modes

  • Checking the schema. Fields exist in schemas and are null in rows.
  • Accepting the agent identity as the principal. It answers "what", never "who".
  • Recording "add more logging". Name the three fields or nothing changes.

Three ways to do this lesson. Pick one. They run the same procedure against the same code — what changes is who drives it, and how much you see.

1In your AI agent — you are taught

The agent reads the lesson, explains the idea, fetches the code, runs the check and answers your questions as you go. This is the one to take if you are here to learn.

Install it, once. From the folder you cloned. --all does every agent it finds on your machine:

python3 scripts/install_skills.py --all --lessons B1.14

Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.

Then pick b1-14-repudiation-and-untraceability:

agentinstall just for this agentstart it
Claude Codepython3 scripts/install_skills.py --tool claude --lessons B1.14type /b1-14-repudiation-and-untraceability
GitHub Copilot (VS Code)python3 scripts/install_skills.py --tool copilot --lessons B1.14type /b1-14-repudiation-and-untraceability in Chat
Cursorpython3 scripts/install_skills.py --tool cursor --lessons B1.14type / in Agent chat and search for it
Codexpython3 scripts/install_skills.py --tool agents --lessons B1.14type $b1-14-repudiation-and-untraceability, or run /skills

2As one Python command — you just run it

No agent, no conversation. The same lesson executes and prints the same readback. Take this one if you have no assistant, if you are reviewing rather than learning, or if you want the result without the teaching around it.

python3 scripts/lesson.py B1.14

3Read the code, and the difference — nothing runs

Neither of the above, and no model needed. The first command writes CyberTravels exactly as it stood at the end of this lesson; the second prints the diff — the lines this lesson added or changed, and nothing from any later one.

python3 scripts/checkpoint.py --at B1.14 --out work/cybertravels
python3 scripts/checkpoint.py --at B1.14 --diff

On Windows, type python wherever these say python3 — and use \ instead of / in the paths. Everything else is identical. A0.0 step 2 explains why.

Ways 1 and 2 need a model; way 3 does not. A signed-in Claude Code CLI needs no API key, and any OpenAI-compatible endpoint works too. With neither, the audit exits 2 and says so rather than inventing an answer. A0.0 sets this up.

Ways 1 and 2 both end with the same readback: What I did, What changed, The number and Read this next. A step that was skipped or refused is reported as exactly that, never as a pass.

If you did the last lesson’s challenge, expect this to stop. Most challenges ask you to edit the code, and ways 1 and 2 will not overwrite your edits: you get stopped, not a failure, with two options — --out for a fresh folder that keeps your work, or --force to discard it and start this lesson clean.

✓What you just proved

A complete-looking tool-call log answers none of the three questions an investigation needs — which user, what motivated it, which hop originated it — because the principal, the motivating input and the delegation chain were never recorded.

✎Your turn

Take yesterday's agent logs and try to answer 'which user caused this action'. Time how long it takes. That number is your time-to-attribution during an incident, when it will be worse.

Tools used

OpenTelemetry

Skill source: skills/threats/audit-answerability-check

← PreviousB1.13 · Resource overload