The audit trail
This page explains what the Better Comply audit trail records, why a failed audit can abort the action that triggered it, and why authenticated users can never change an audit row.
Quality and compliance owners, auditors, and administrators. To read the log day-to-day, see Reading the audit log.
What the audit trail is for
21 CFR Part 11 §11.10(e) asks for a "secure, computer-generated, time-stamped audit trail" of operator actions. ISO 9001 and ISO 13485 ask for records of changes to controlled documents and competence. The Better Comply audit log (audit_logs) is that record. Every entry carries:
- Who did it (the acting user).
- What they did (an action from a fixed taxonomy, for example
complete_training,approve_document). - When (a server-generated timestamp).
- On what (the entity type and id, for example a training version or a document).
- Details (a structured payload describing the change).
The IP address recorded against signature events is captured server-side. See Electronic signatures.
One write path, no back doors
Audit rows are written by a single server-side function (log_audit_event, a SECURITY DEFINER routine). It is the only path an authenticated client has to the table. The permissive insert policy that once allowed direct writes was removed, and the table now carries explicit deny-all policies for INSERT, UPDATE, and DELETE by authenticated users.
In practice this means three things:
- A user cannot forge an audit row by writing to the table directly.
- A user cannot edit an audit row to change what it says.
- A user cannot delete an audit row to hide an action.
Database triggers on regulated tables also write audit rows automatically, capturing the previous and new values of a changed row.
The audit log is append-only. There is no supported way for any authenticated role to update or delete an entry. A correction is recorded as a new entry that references the original, never an overwrite. This is the guarantee an auditor relies on.
Fail-loud: a failed audit aborts the action
For routine operational events, Better Comply records the audit row on a best-effort basis: if the write briefly fails, the event itself still happens.
For compliance-critical events, the rule is the opposite. If the audit row cannot be written, the function throws and the surrounding action is rolled back. The principle is simple: no regulated record exists without its audit entry. A signature that cannot be logged is not a signature.
A compliance-critical action that cannot be audited does not "succeed quietly". It aborts. You will never have an approved version, a signed evidence record, or an effective document without a matching audit entry, because the system refuses to create one.
Fail-loud actions
These actions always fail loud. They cannot be downgraded to best-effort by the caller.
Training and evidence
create_evidencecomplete_trainingsign_evidencecreate_versionactivate_versionapprove_contentresolve_review_taskissue_certificate
Controlled documents
create_controlled_documentdelete_controlled_documentupload_document_revisionedit_document_contentsubmit_document_for_reviewapprove_documentreject_document_revisionmake_document_effectiveobsolete_document
For controlled-document actions, the server route that performs the change reverts its own work (the database rows and any storage blob) if the audit write fails, so the document never lands in a state with no recorded justification.
Best-effort actions
These are operational events. The action proceeds even if the audit write briefly fails, because they are high-volume and the action's own record is the durable artefact.
loginandlogout- Quiz pass and fail
- Onboarding-rule edits
acknowledge_document(read-and-understood) - the acknowledgement row itself is the regulated artefact, and millions of rows are normalsend_supervisor_report- an operational notification; the delivery ledger row is the durable artefact
A read-and-understood acknowledgement is captured as its own append-only row, with a uniqueness constraint and a delete block. That row is the record an auditor needs, so the separate audit entry is operational rather than load-bearing.
The action taxonomy
Each action name is a fixed value, so an auditor can filter the log deterministically. The training lifecycle uses one set of action names (create a version, approve content, activate a version, complete a training, sign evidence, issue a certificate) and the controlled-document lifecycle uses its own set (submit for review, approve, reject, make effective, obsolete, upload a revision, acknowledge). The two sets are kept distinct so a transition is never ambiguous.
What to tell an auditor
- Every regulated change is in
audit_logs, written by one server function. - The most important changes fail loud: the change cannot persist without its audit row.
- No authenticated role can edit or delete an audit row; corrections append.
Related
- Reading the audit log - filters and what each action means.
- Exporting evidence - pulling the log for an inspection.
- Evidence and immutability - how the records the log points to are themselves frozen.
- Preparing for an audit.