Audit trail
The audit trail (labeled “Audit trail” in the interface) is an immutable journal into which X7 Insight records every significant change: who made it, when (in UTC), what it was before and after, and — where required — for what reason. Nothing in the journal can be edited or deleted; you can only append new records. This is the foundation of study-data reproducibility; the journal is designed for the requirements of 21 CFR Part 11 and EU Annex 11.
Append-only and the hash chain in plain terms
Section titled “Append-only and the hash chain in plain terms”Two guarantees make the trail trustworthy:
- Append-only. Journal records cannot be changed or deleted — not by a user, not by an administrator, not even by the database administrator. Correcting an error is always a new record on top of the old one, never a substitution of the old one.
- Hash chain (cryptographic linking). Each record carries a “fingerprint” (hash) whose computation includes the fingerprint of the previous record. The records are linked into a continuous chain. If someone tries to change, delete, or insert a record in the middle, the fingerprints stop matching, and verification detects it immediately.
What happens when a value is written
Section titled “What happens when a value is written”Saving a single value is not one operation but a fixed chain of reactions, carried out in full within one transaction: either all of it goes through, or nothing changes at all. The audit write is a mandatory link in this chain, not a separate “log afterwards”.
-
Permission check (PBAC). Do you hold the permission to write to this field and its scope? If not, the write is refused and the value does not change.
-
Lock check. Are the field or its scope locked? Under a hard lock a write is impossible.
-
Rule gates. You cannot write to a computed (derived) field, nor to a field hidden by a skip-logic condition.
-
Value validation. Type, range, and code-list membership — per the field definition.
-
Reason for Change (RfC). Editing an already-saved value requires a reason; an initial entry does not.
-
Writing the value. The value is saved with an optimistic version check and a reset of dependent axes: SDV, medical review, and coding return to their initial state, and a completed form moves back to “in progress”.
-
Audit write.
audit.append()— in the same transaction. There is never a changed value without an audit record. -
Signature invalidation. Every signature that covered this value automatically becomes invalid.
-
Rule engine. Derivations are recomputed in a cascade (actor — the system), then the edit checks run: block rolls back the entire write, query opens an auto-query.
-
Event publication. Events go into the transactional outbox; subscribers (modules) receive them via a background worker — only after the transaction commits.
What enters the audit trail
Section titled “What enters the audit trail”All changes to data, metadata, configuration, access, and work objects are audited, along with a closed list of privileged reads. Ordinary reads are not audited (otherwise the journal would drown in noise).
| Category | Example actions |
|---|---|
| Clinical data | entering and editing a value, clearing it, marking “not done”, creating a subject, changing its status, creating a visit, completing a form |
| Metadata (design) | creating and publishing a design version, editing a form or an edit check in a draft, approving a translation |
| Work objects | opening/answering/closing a query, applying and invalidating a signature, placing and releasing a lock |
| Access | granting and revoking grants, creating and assigning roles, overriding an SoD conflict |
| Configuration | changing a setting, registering/enabling/disabling a module, consenting to a module |
| Privileged reads | an unblinded read of a blinded value, launching an export, exporting the audit trail itself |
What one record contains
Section titled “What one record contains”Each journal record describes one action with a set of fields (in the interface — columns and row details):
| Field | What it shows |
|---|---|
| Occurred at (UTC) | the moment of the action in universal time; the local representation is for convenience only |
| Actor | who did it: a user, a module, or the system |
| Action | what happened (for example, “data saved”, “signature applied”) |
| Entity | which object it acted on (a value, a form, a subject, a grant…) |
| Scope | the scope path: study → site → subject → form → field |
| Before / After | the canonical values of exactly the fields that changed (not the whole record) |
| Reason | the Reason for Change text, where it is required |
| Module | if a module performed the action — which one |
| Caused by | if the action is an automatic reaction (for example, a signature reset), a reference to the originating event |
Reason for Change
Section titled “Reason for Change”X7 Insight distinguishes a first entry from an edit of an already-saved value:
- First entry (the field was empty) does not require a reason — this aligns with the ALCOA principle of “contemporaneous” recording.
- Any change to a saved value, including clearing it, requires a reason. Without a reason the system will not save the edit. The interface shows a dialog with a free-text field and a per-study configurable list of common reasons.
A reason is also required for: releasing a lock, clearing an SDV mark, cancelling and reopening a query, rejecting a query answer, overriding an SoD conflict, and emergency unblinding. For how this looks during data entry, see Data entry.
The Audit trail: how to read it
Section titled “The Audit trail: how to read it”The same viewer opens at different levels; the level determines which records you see (and
requires the corresponding audit.read permission in that scope):
- The whole tenant — the Admin → Audit section: the immutable journal of the entire organization.
- A study — the Audit tab inside a study: only its records.
- A subject and a form — the same viewer opened from a subject or form page: narrowed to the history of a specific participant or a specific form.
Filters above the table: the period “From (UTC)” — “To (UTC)”, Actor, Action, and Entity type. Columns: Occurred at (UTC), Actor, Action, Entity, and Scope. Clicking a row reveals the “Before / After”, “Reason”, “Module”, and “Caused by” details.
Integrity verification (verify hash-chain)
Section titled “Integrity verification (verify hash-chain)”The Verify trail integrity button triggers a recomputation of the whole chain from the start.
The permission to run it is audit.verify. The result:
- Chain intact — “N records verified”. No record has been changed, deleted, or inserted in the middle.
- Chain broken — the sequence number (seq) of the first broken record is reported. Verification catches content edits, record deletion, insertion in the middle (a gap in the numbering), and substitution of a reason.
An additional layer of protection is anchoring: X7 Insight periodically records the “head” of the chain (its current fingerprint) in an external write-once store. This makes it possible to prove integrity even if you assume that someone with database access recomputed the entire chain. The full anchor history is included in every inspection packet and closeout package.