Skip to main content

The audit trail

Every gate decision is recorded, allowed or refused. A trail that logged only what happened would not answer "why did it not do the thing I asked", which is most of what anyone asks it.

Reading it

WhereHow
in a session, liveCtrl-T toggles the trail
a one-shot run--trace, which puts it on stderr
after the fact~/.bravebot/sessions/<directory>/<id>.audit.jsonl

An incognito session shows its gate decisions on screen as always and writes no file. The trail holds no content, but it does hold gate names and paths, which is a record of a session having happened and what it touched.

bravebot "what does this do?" --file src/main.rs --trace

What a trail looks like

Reading a file in a trusted directory, where the content reaches the model:

ok precommit: routing fields ["task"] fixed before any observation
ok promote: read_file.path proposed by the model, confined and non-destructive
ok file_read.path [routing] (T,pub)
observe file_read produced (T,priv)
ok trust: notes.md read as trusted, from a trusted path
ok render: read_file: content reshaped for presentation, still (T,priv)
ok present: tool_result: notes.md is (T,priv), so the planner may read it

Three pieces of notation appear throughout:

  • (T,pub) and (U,priv) are the label on a value: trusted or untrusted on the first axis, public or private on the second.
  • ref:N is a slot holding content the planner is not allowed to read, so it is handed the reference instead of the bytes.
  • routing marks the part of a call that decides where it lands, as opposed to the part that is merely carried.

A gate is a check that has to pass before anything consequential happens: content reaching the model, a file being written, a program being run, a request leaving the process. Each one decides a single question and refuses rather than warning, so there is no path to a consequence that does not go through one.

Compaction is recorded

Every compaction gets a line: how many messages were summarised, how many were kept word for word, what the summary cost, and which tool-calling round it landed on. A /compact you asked for between rounds reports round zero, having interrupted nothing.

It is recorded after the conversation is shortened, so a summary refused on the way back in leaves no line claiming one was made. Counts and nothing else, so this carries no more content than the rest of the trail does.

The trail holds no content

Every field is a gate name, a capability, a label, a path or a slot id. That is exactly why it can be put on your screen and written to a file without any release, and it is what makes the record safe to keep for a workspace nobody vouched for.

Assertions are recorded as assertions

Vouching for the output of a command you typed, labelling your configuration, and admitting a pasted picture are each written down, because each is a claim a human made rather than something the system worked out. These are the points where trust enters from outside, and a trail that recorded only what the system deduced would omit exactly the decisions somebody might later want to account for.

On disk

One JSON object per line, appended a turn at a time, so a line-oriented file can be read with whatever is to hand:

jq -r 'select(.gate == "present")' ~/.bravebot/sessions/*/…​.audit.jsonl

The labels are spelled out in words rather than abbreviated, because a file read months later has no legend beside it, and each event keeps the time it happened. The compact form suits a terminal, where the reader has the legend in front of them.