Skip to content

Audit logs

For production platforms, auditing is a foundational compliance requirement (supporting SOC 2, ISO 27001, and GDPR). The platform provides a tamper-evident, cryptographically chained audit log of every state-changing operation performed by users, services, or external systems acting on your account.

To prevent log manipulation, deletion, or backdating, the audit log functions as an append-only ledger structured as a hash chain, rooted at the account:

  1. Every audit event receives a sequential sequence number (seq) scoped to your account. One account is one chain, and everything that happens on it is in that chain.
  2. The genesis event for an account starts at seq = 1 with a prev_hash of NULL.
  3. Every subsequent event (seq > 1) computes its cryptographic signature (row_hash) as a SHA-256 hash of its event fields concatenated with the row_hash of the preceding event:
    row_hash = SHA256(id || account_id || actor_id || action ||
    resource_kind || resource_id || metadata ||
    created_at || prev_hash)
  4. If an actor attempts to delete or alter a past audit row, the cryptographic link breaks, invalidating the entire subsequent hash chain. A verification job runs regularly to assert the integrity of every account’s chain.

Atomic transactions (Failed audit = failed operation)

Section titled “Atomic transactions (Failed audit = failed operation)”

An audit log is only useful if it is complete. The platform ensures this through strict atomic database bounds:

  • Every state mutation and its corresponding audit event insertion are performed inside the same database transaction.
  • If writing the audit log fails (e.g., due to database constraints, lock contention, or invalid fields), the transaction is rolled back, aborting the business mutation itself.
  • To handle concurrent operations safely without hash chain forks, the platform acquires a transaction-scoped database advisory lock on the account ID. This serializes audit writes for a single account without blocking other tenants.

Every audit event is attributed to a specific principal:

Actor type Wire representation Description
User <user_id> (bare sub) A human operator acting through the portal.
Service service:<name> Internal system processes (e.g. the audit-chain sweep or the deletion saga).
External external:<name> Third-party services acting via webhooks (e.g., Stripe processing payments).

The platform logs events across a strict, closed vocabulary of verbs and resource types:

  • created: A resource came into existence (e.g. inviting a member).
  • updated: A resource’s state was mutated (e.g. changing a member’s role).
  • deleted: A resource was removed (e.g. removing a member).

Auditing covers all system resources:

  • account (the tenancy root and billing container)
  • member (somebody you gave access to your account)
  • token (tor_ API tokens)
  • webhook (outbound integrations)
  • plan (subscription tiers)

The set is closed at the writer: it is a Rust enum, so a mutation cannot invent a resource kind that this list does not name.

Two ways, and they read one query so they cannot disagree about a row.

In the console, at Audit log under Account, or g then l. The most recent 100 events, newest first. Every role can read it: a log only one person can see is not evidence a team can act on.

Through the API, GET /v1/audit with your tor_ API token — your account’s events, newest first, with a limit that defaults to 50 and is capped at 200 rather than refused. See API tokens for creating one.

Both carry prev_hash and row_hash on every row. That is what lets an exported page be re-verified by somebody who does not trust us, and it is the difference between an audit log and a list of events we assert is complete.

The log is written whether or not anything is reading it: every mutation commits its audit row in the same transaction as the change it records. This page and the API lane were both removed in September 2026 and restored the same month; not one row was lost, because the record and its surfaces are different things.