Audit trail

Every answer, and every refusal. Written down before anybody asks to see it.

Most access logs record what worked. This one records what did not, with equal care, because a pattern of refusals is the more interesting signal – somebody probing at the edge of their access looks exactly like nothing at all in a log that only writes successes.

Most systems log what they did. The refusals are the half that proves the rules were running.

audit · planning-testillustrative
09:14 – 09:22
6 calls · 3 allowed · 3 refused 3 allowed · the ones a success-only log would show 3 refused · the ones it would not
TimePersonToolOutcomeReason
09:14:02a.morgandescribe_modelallow–
09:14:38a.morganread_sliceallow–
09:15:11a.morgancrm_read_alldenyskill_not_enabled
09:15:11t.walshdocs_searchallow–
09:16:40t.walshread_slicedenyno_grant_on_project
09:22:03––denycredential_rejected
Half of this window is refusals, and that is the point. Press allowed and you are looking at what a success-only log would have recorded: two people working, nothing else. Press refused for the half it would have thrown away, which is a skill somebody does not hold, a project somebody has no grant on, and a credential rejected before it reached any check at all.

The last row has no person on it on purpose. A credential rejected before it reached any permission check is still recorded, so somebody guessing at your connector is visible rather than silent.

The reason codes

Twenty names, and a test that freezes them.

A refusal reason is a stable name, not a sentence someone rephrased. There are twenty of them, and a test fails the build if one changes.

Why not a message

A human-readable message is written for whoever is debugging that week. It gets reworded, it gets translated, and every rewording silently breaks the report somebody built on top of it.

Why a test rather than a convention

Conventions are followed until a deadline. The frozen set means renaming a code is a deliberate, visible act with a failing build attached, rather than a tidy-up in a pull request nobody reads closely.

What you do with them

Group by code. A rise in one specific refusal is a person hitting a boundary repeatedly, and it usually means a grant is wrong rather than that somebody is malicious.

This is data somebody will read three years from now. It has to still mean the same thing then.

Which is the whole argument for a frozen name over a readable sentence. A message gets reworded by whoever is debugging that week, and every rewording silently breaks the report somebody built on top of it.

The record

What a row contains. And what it deliberately does not.

The absence of figures is a deliberate trade. It means the audit trail cannot tell you what a person saw, only what they were permitted to ask for. Recording the values would make the log a second copy of your data, with all of the exposure and none of the controls.

The struck-through half is the deliberate part. The row says a read was permitted to happen. It does not say what came back, which is what keeps the log from becoming a second copy of your data.

On every row

  • When – to the second.
  • Who – the verified person, by their own identity rather than a shared credential.
  • Which environment – never inferred, always named.
  • Which tool was called.
  • The outcome – allowed or refused.
  • The reason – one of the twenty names, on every refusal.

Never on a row

  • The figures that came back. The record says a read happened, not what it returned.
  • Document contents. Same reason.
  • Anything about another organization. Not a name, not an identifier, not a slug.
  • Free-text somebody can shape. A log line an attacker can write into is a log line you cannot trust.

Access to the record

It is yours, and reading it is itself governed.

QuestionAnswer
Who can read it Your own administrators, through the console, scoped to your organization. There is no cross-organization view, including for us.
Can you export it Yes. It is your record and it leaves in a form you can put in front of an auditor.
Can you edit it No. Neither can we, through the console.
Does it survive a revoked grant Yes. Removing somebody's access does not remove the history of what they reached, which is the point.

One honest caveat about permanence. The console offers no way to alter a row, and no feature exists to do it. That is an application-level guarantee, not a cryptographic one: it is a database, and a database administrator with direct access is a different threat model. If write-once storage is a requirement in your review, say so early rather than assuming it.

The export

The export is the page's own query. Which is a stronger promise than it sounds.

Most products build an export separately from the screen it exports. The two then drift, and the drift is always in the same direction: the screen shows something the file leaves out. Here they are one query, so a row the page withholds cannot appear in the file, and a row the file omits cannot be on the page.

What comes out

  • Every row you can see, filtered exactly as you filtered the screen, in a form you can hand to an auditor without reformatting it.
  • Refusals included, with their reason codes intact. An export of successes only would answer the least interesting half of the question.
  • Nothing that was not on the screen. No hidden columns, no wider scope than the view you exported from.

What there is not

  • No streaming into your SIEM yet. The export is something a person runs, not a feed you subscribe to. If continuous ingestion is a requirement, it is a build rather than a setting, and we would rather say that before you plan around it.
  • No alerting on a refusal. The findings sweep raises configuration issues for your administrators to acknowledge; it does not page anybody at three in the morning.

Two things people assume are missing

Rejected credentials. And how long any of it is kept.

A rejected credential is recorded too

A request whose token fails validation never reaches the part of the system that decides permission, so in most designs it lands in an application log and nowhere a client can read. It is written to the trail here, with no person attached because there is no verified person. The practical consequence: somebody running credentials against your connector is visible to you, in the same place as everything else, rather than to us in a log file.

Retention, and why there are two answers

Your business data has no retention period here because no copy of it is kept. The trail itself is retained on a period agreed with you and pruned by a job that runs as a different database principal from the web application, so a compromised web process cannot erase what it did. Both halves of that are in data handling with the current numbers, which is where they can be kept correct.

Ready?

See what a month of it looks like. Let's put it to work.

The most useful thing in an evaluation is usually not the demo. It is a fortnight of real rows from your own environment, and the conversations that start when somebody reads them.

Why this exists: useful assistants connect an answer to an action, and an action to a completed job.