Skip to content

The audit log

The audit log is a read-only, append-only record of every mutating change in PrepTable. It exists so a question like "who changed Harlingen's par level for yellow onions last Tuesday?" answers itself from a single screen rather than a Slack thread.

What's recorded

Every route that mutates data goes through an audit(...) middleware in the API that writes a row with:

  • When — UTC timestamp, millisecond precision
  • Who — the user's ID and email at the moment of the change (resolution captured at write time, so a later rename or deletion doesn't break the historical record)
  • What — the entity table and primary key (e.g., items, id 42)
  • The actioncreate, update, delete, login, register, login_failed, role_change, campus_assigned, …
  • The diff — for update, both the prior and new values for the changed fields. Deletes record the entire prior row.
  • The request — the IP, the User-Agent, and the route path

Read endpoints are deliberately not logged — the audit log records who changed what, not who looked at what (which is a different question with different privacy and retention properties).

How to read it

The log lives at Admin → Audit log in the navigation. The default view is a reverse-chronological list with filters along the top.

The filters are:

  • Date range — defaults to the last 24 hours
  • Actor — a specific user (by email)
  • Action typecreate, update, delete, login, auth_*, etc.
  • Entity typeitems, meals, purchase_orders, users, etc.
  • Entity ID — to follow one specific record through its history

Click any row to see the full diff. For an update, you'll see a side-by-side of "before" and "after" for every changed field.

Common workflows

"Who changed this?"

  1. Open the log, filter to the entity type and ID (the recipe, the item, the user).
  2. Read backwards from the most recent change to see the full history.
  3. The actor, action, and timestamp are all on the row.

"Has anyone logged in today from an unexpected IP?"

  1. Filter to action login for today.
  2. Scan the IP column. Distinct IPs across multiple users in a short window is a strong signal of credential reuse or compromise.

"Show me everything this manager did last week"

  1. Filter actor = the manager's email.
  2. Set date range to last week.
  3. Export the filtered list to CSV if you need to share it.

Retention

The audit log is retained for 12 months on the production database and is included in the standard database backup. There is no automatic purge — older rows stay queryable.

If your customer's data-retention policy is more or less than 12 months, raise the question in the engagement channel; the retention window is configurable per deploy.

What is not in the audit log

  • Read access. We don't log "user X looked at record Y" because the privacy profile of read access is meaningfully different from write access, and the volume is much higher. If you need read access tracking for a specific compliance regime, that's a known follow-on (raise it through the engagement channel).
  • Failed actions. We log login_failed because that's a security signal, but we don't log every 4xx response from the API. The same compliance question applies.
  • Changes made via the seeded demo data generator (seedgen). The generator writes directly to the database and bypasses the audit middleware; that's intentional so demo data doesn't pollute production audits. If the generator runs in your environment, do not rely on the audit log for "did anyone use the demo tool" — check the deploy log instead.

Where to go next

Built for campus dining operations teams.