Who asked the assistant to do that
An audit record says who did what and when, without keeping the private text. It matters because, when something goes wrong, you need to reconstruct what happened without having copied everyone's messages.
A tool refuses a strange request on Tuesday. On Friday someone asks why. A record with a user id, a tool name, an outcome and a time answers that. A copy of the chat would answer it too, and become a liability.
By the end of this lesson
Note
I can write audit records with no private content and delete them when their retention time ends.
Key words
| Word | What it means | Example |
|---|---|---|
| Audit record | a note of who did what, when and with what result | |
| Retention | how long a record is kept | |
| Purge | to delete the records that are past their time | |
| Fake clock | a time value your code sets, so a test controls time | |
| Identifier | a code that stands for a person without naming them | u-17 |
The cells students fill in are left blank in this preview.
Record who, what and when
Write one small record for each thing the feature did. Keep to a fixed list of fields. Anything else, such as a prompt or a reply, is dropped before the record is saved.
Use an identifier for the person, not a name. Append records and never edit them, so the log can be trusted.
Worked example: one audit record
| Field | Example | Why it is there |
|---|---|---|
| at | a time | When it happened |
| user | u-17 | Who asked, without a name |
| action | read_note | What the feature did |
| outcome | allowed | What the checks decided |
| tokens | 120 | How much it cost, in a count |
| (not kept) | the prompt text | It may hold personal data |
The cells students fill in are left blank in this preview.
Keep records only as long as useful
Every record has a reason, and the reason ends. Pick a retention time for each kind: a short one for chat text, a longer one for audit records. Then delete what is older.
Legal duties vary by country and by field, so check the ones that apply to you before you choose a number.
Write the audit script, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const DAY = 24 * 60 * 60 * 1000;' 'let now = Date.UTC(2026, 0, 1);' 'const allowed = [\"user\", \"action\", \"outcome\", \"tokens\"];' 'const log = [];' '' '// An audit record holds who, what and when. Never the text.' 'function record(fields) {' ' const clean = { at: now, id: log.length + 1 };' ' for (const key of allowed) {' ' if (key in fields) clean[key] = fields[key];' ' }' ' log.push(clean);' ' return clean;' '}' > audit.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the audit script, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'record({ user: \"u-17\", action: \"read_note\", outcome: \"allowed\", tokens: 120, prompt: \"Hi I am Amira\" });' 'now = now + 20 * DAY;' 'record({ user: \"u-18\", action: \"run_shell\", outcome: \"refused\", tokens: 0 });' 'now = now + 20 * DAY;' 'record({ user: \"u-17\", action: \"add_numbers\", outcome: \"allowed\", tokens: 60 });' 'console.log(\"Fields kept: \" + Object.keys(log[0]).join(\", \"));' 'console.log(\"Prompt stored: \" + (\"prompt\" in log[0]));' >> audit.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the audit script, part 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Retention: keep a record for 30 days, then delete it.' 'const KEEP = 30 * DAY;' 'function purge(list, at) {' ' return list.filter(function (item) { return at - item.at <= KEEP; });' '}' 'const left = purge(log, now);' 'console.log(\"Before: \" + log.length + \", after: \" + left.length);' 'console.log(\"Kept ids: \" + left.map(function (item) { return item.id; }).join(\", \"));' >> audit.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run the audit script
docker exec -w /home/node/project agent node audit.jsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
What the script prints
| Line | What it shows |
|---|---|
| Fields kept: at, id, user, action, outcome, tokens | The prompt field was dropped |
| Prompt stored: false | Nothing private reached the log |
| Before: 3, after: 2 | The first record is 40 days old and the limit is 30 |
| Kept ids: 2, 3 | The two recent records survive |
The cells students fill in are left blank in this preview.
A list stands in for real storage
The script keeps its log in an array and moves a fake clock by hand. A real system uses a database, with backups and access control, and a scheduled job that purges.
Backups matter too. A record deleted from the database is still in last month's backup unless the backup expires as well.
A common mistake
Warning
Many people think that more logging is always safer. A log that keeps every prompt is itself a store of personal data, and it must be protected and deleted like one. Log the fields you will need to answer a question, and no more.