Skip to content

Audit trail & e-signatures

Changes are appended, never edited. Signatures say what they mean.

Changes to quality records land in an audit trail the database will not let anyone edit or delete: who, when, which record, and the value before and after. At every sign-off point in the app, the signer re-enters an e-signature PIN, and the signature records its meaning.

Anatomy of an entry

One entry, taken apart

A real update to a nonconformance in the demo tenant, drawn as the audit log draws it. Each numbered part is a column on the audit row.

Audit log › NC-1227

Update

NC-1227Nonconformance

by Astrid Lindqvist29 Sept 2026, 18:56

Before: Cosmetic defect above acceptance limit on Type I Borosilicate Vial 2R lot V2R-2478 Detected during routine operations at Uppsala Manufacturing. Immediate assessment performed and the affected material identified.

After: Cosmetic defect above acceptance limit on Type I Borosilicate Vial 2R lot V2R-2478 Detected during routine operations at Uppsala Manufacturing. Immediate assessment performed and the affected material identified. Update: 100% visual re-inspection of the 2,000-unit AQL sample found 41 units with airlines and 6 with stones (limit: 0 critical, 10 major); the defects cluster on two mould numbers (M07, M12), which points to the supplier forming line rather than transport damage.

Also on the row, not drawn in this view:ip_addresscompany_id
  1. What happenedaction

    Create, update or delete, or a named step such as Close, Cancel or Approve when a status change maps to one.

  2. Which recordentity_type · entity_id

    The record type and the record, shown by its number and linked to the record itself.

  3. Whoperformed_by

    The signed-in user whose session made the change. When a scheduled job changes a record, the entry is kept and reads “System”.

  4. Whenperformed_at

    The server’s clock when the entry is written, just after the change commits. Never the user’s device clock.

  5. Before and afterold_value_json · new_value_json

    The tracked fields as they were and as they became. For a nonconformance that is status, title, description, owner, site and department.

  6. Where fromip_address

    Stored on every entry. It is not drawn in this view, but it goes into the CSV export and the printed record.

How the entry gets written

  1. The record changes

    From a screen, the API or a scheduled job. The table carries its own audit trigger.

  2. The entry is queued with it

    The trigger queues the entry inside the same database transaction, so a change that rolls back leaves no entry behind.

    Database refuses
  3. The worker writes it

    Just after the change commits. Ids in the values are resolved to readable names where it can, and secrets such as passwords are redacted.

  4. It can only be read

    An update or delete on an audit entry is refused by a trigger on the audit table.

    Database refuses

Append-only, stated exactly

Entries are added and never altered. The audit table refuses every update and every delete, whoever sends it.

Database refuses

Is the trail hash-chained?

No. Entries are not cryptographically chained to one another, and we do not call the trail tamper-proof. What protects them is the database refusing any update or delete on the audit table, and the absence of any edit screen or write API for it. Signatures carry their own SHA-256 hash of the signing context, which is a separate thing.

Anatomy of a signature

Prove it is you, say what you mean, and it is written against one record

Approving a document, closing a CAPA or nonconformance, disposing of a retain sample, recording a calibration and approving a log entry all open the same signature step.

  1. Step 1: Re-authenticate

    The app asks the signer for their e-signature PIN, a secret separate from their login password. Being signed in is not enough to sign.

    • Five wrong PINs lock PIN signing for that user for 15 minutes
    • The signature service can also verify a password or a Google or Microsoft token
    Server refuses
  2. Step 2: State the meaning

    The signature records what it asserts, in its own column, set by the action being signed: closing an NC signs Closed, a disposal signs Disposed, a calibration signs Performed.

    Meanings in use

    • Approved
    • Rejected
    • Reviewed
    • Verified
    • Closed
    • Cancelled
    • Disposed
    • Performed
  3. Step 3: Write the row

    One row in the signature ledger: the signer, the meaning, the time, the IP address and browser, and a SHA-256 hash of that signing context. It names exactly one thing it was given for.

    • The hash covers the signing context, not the record’s content
    • The signature row is itself audited: signer, meaning, time, hash and subject
    • Revoking a signature marks it revoked with a reason; the trail records the revoke
  4. Step 4: See it on the record

    The record’s audit history shows the approval. Printed records carry an Approvals & Signatures block, built from those approval entries: action, signer, date and IP.

    • The printout appends the record’s recent audit history
    • A copy printed by someone without audit-trail access says the block was withheld, so it is never mistaken for an unsigned record
One row in the signature ledgersignatures
Signeruser_id
The re-authenticated user. (step 1)
Meaningmeaning
What the signature asserts. (step 2)
Signed atsigned_at
Server time at signing.
Fromip_address · user_agent
The IP address and browser it came from.
Hashpayload_hash
SHA-256 of the signer, meaning, time, IP and, for most subject types, the subject. (step 3)
Revocationis_revoked · revoked_reason
A revoked signature stays on the row, marked, with its reason.

Exactly one subject

Every row names one, and only one, of these. A row naming none, or two, is refused.

  • nc_id
  • capa_id
  • change_request_id
  • quality_event_id
  • specification_id
  • sampling_plan_id
  • retain_sample_id
  • equipment_id
  • record_id
  • field_record_revision_id
  • assignment_instance_id
  • task_instance_id
  • workflow_instance_step_id
Database refuses

Not in the ledger: training completions, which are verified by a manager on the training record. Only the audit trail is append-only; see the FAQ.

Straight answers

What auditors and IT ask first

What does this do for 21 CFR Part 11?

It supplies technical controls that Part 11 addresses for audit trails and electronic signatures: an append-only trail with before-and-after values, and signatures with re-authentication, a recorded meaning and the signer’s identity on the record. Whether your system meets Part 11 depends on how you validate and operate it. Software alone cannot make that claim for you.

Are reads logged?

No. The trail records changes (creates, updates and deletes) on the tables that carry an audit trigger. Opening or viewing a record is not an audit event.

Is every field of every table in the trail?

No. Each audited table tracks a configured set of fields, the ones that matter for that record, and ignores housekeeping timestamps. Secrets such as passwords, tokens and assessment answer keys are redacted: the trail shows that they changed, not their values.

Is every action signed, with a reason for change?

No. Signatures are taken at defined sign-off points such as approvals, closures, disposals, calibrations and log entry reviews. Ordinary edits are captured in the audit trail with their before-and-after values rather than a mandatory reason for change.

Can a signature be deleted?

There is no delete action for signatures. A signature can be revoked by its signer or the company owner, with a written reason; the row stays, marked revoked, and the revoke is written to the audit trail. The signatures table does not carry the database trigger that the audit table does, so we describe only the audit trail as append-only.

The rules

What the trail and the ledger refuse

Edit or delete an audit entry
A trigger on the audit table refuses every update and delete. There is no edit screen for the trail.
Database refuses
Write an audit entry by hand
The app’s GraphQL API exposes no insert, update or delete on the audit table. Entries come from the audit triggers.
Server refuses
A signature that names no record, or two
A check on the signatures table requires exactly one subject column to be set on every row.
Database refuses
Who reads and exports the trail
Reading the audit trail and exporting a record’s history to CSV are separate permissions you grant by role.
You configure

Show an auditor who changed it, and who signed it.