> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auditrails.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Core Concepts: Events, Hash Chains, and AuditRails Storage

> Understand how AuditRails structures organizations, projects, events, hash chains, dual-tier storage, and plan-based retention policies.

Understanding a handful of core concepts will help you design your audit logging integration correctly from the start. This page explains how AuditRails structures your data, how the tamper-proof hash chain works under the hood, where your logs are physically stored, and how long they're kept depending on your plan.

***

## Organizations

An **Organization** (also called a Tenant) is the top-level account boundary in AuditRails. Everything — your projects, events, API keys, billing, and team members — lives inside one organization. Each organization has its own independent hash chain, so events from different customers or tenants never intermingle.

### Plans

| Plan              | Frameworks                            | Best For                                   |
| ----------------- | ------------------------------------- | ------------------------------------------ |
| Starter Trails    | 1, chosen at signup and locked        | Small production applications              |
| Framework Trails  | Up to 3, add/remove/swap self-service | Growing teams with multiple services       |
| Compliance Trails | All 18, unlimited                     | Regulated industries, long-term compliance |

All plans include the same 180-day base retention — see [Retention](#retention) below for how enabling a compliance framework can extend it.

### Roles

| Role        | What They Can Do                                                                 |
| ----------- | -------------------------------------------------------------------------------- |
| **Admin**   | Full access: manage members, billing, projects, API keys, and view all logs      |
| **Member**  | Create and manage projects and API keys; view logs scoped to their projects      |
| **Auditor** | Read-only access to logs and compliance reports; cannot modify any configuration |

***

## Projects

A **Project** is a logical grouping of events within your organization. Each project is scoped to one or more API keys, which means events from different applications, environments, or services are cleanly separated.

A common pattern is to create one project per environment:

```
my-app-production   →  at_live_prod_xxx
my-app-staging      →  at_live_stg_xxx
my-app-development  →  at_test_dev_xxx
```

<Tip>
  Use separate projects for each environment so that test events never pollute your production audit trail. Test keys (`at_test_...`) are free and rate-limited independently — they'll never count against your plan's request quota.
</Tip>

***

## Events

An **Event** is the atomic unit of data in AuditRails. When you call `audit.log(...)`, you are writing one event to the chain.

### Fields

<ParamField body="action" type="string" required>
  What happened, in `resource.verb` format (e.g. `user.login`, `document.deleted`, `invoice.paid`). Maximum 255 characters. This is the only required field.
</ParamField>

<ParamField body="actor_id" type="string">
  The identity that performed the action — typically a user ID, service account name, or API client identifier (e.g. `user_123`, `svc-billing`).
</ParamField>

<ParamField body="resource" type="string">
  The object that was acted upon, in `type/id` format (e.g. `session/sess_abc`, `document/doc_789`, `invoice/inv_001`).
</ParamField>

<ParamField body="metadata" type="object">
  An arbitrary JSON object for any additional context — IP addresses, request IDs, before/after values, geolocation, etc. The total request body must not exceed **256 KB**.
</ParamField>

### SDK-Injected Fields

The SDK (or API ingest layer) automatically attaches these fields before hashing. You do not set them manually:

| Field       | Type     | Description                                              |
| ----------- | -------- | -------------------------------------------------------- |
| `log_id`    | ULID     | Globally unique, time-sortable identifier for this event |
| `tenant_id` | string   | Your organization's identifier                           |
| `timestamp` | ISO 8601 | Precise server-side ingest time in UTC                   |

<Note>
  Because `timestamp` is assigned server-side at ingest, it cannot be backdated. This is an intentional integrity guarantee — the chain reflects the true order events were received by AuditRails.
</Note>

***

## Hash Chains

Hash chaining is the core mechanism that makes AuditRails tamper-proof. Every event you log is cryptographically linked to every event that came before it, forming an unbroken chain where any modification — however small — is immediately detectable.

### The Formula

```
hash = SHA-256(previous_hash + canonical_payload + timestamp_ms)
```

* **`previous_hash`** — the SHA-256 hash of the immediately preceding event in your tenant's chain.
* **`canonical_payload`** — the deterministically serialized event fields (action, actor\_id, resource, metadata).
* **`timestamp_ms`** — the server-assigned ingest timestamp for this event, expressed in milliseconds since epoch.

### Genesis Event

The very first event in any tenant's chain uses a **genesis hash** as its `previous_hash`:

```
previous_hash = "0000000000000000000000000000000000000000000000000000000000000000"
```

That is 64 hexadecimal zeros — a well-known constant that anchors the chain.

### Tamper Detection

Because each hash depends on all previous hashes, modifying any single event changes its hash, which changes the next event's expected input, which invalidates every hash that follows. AuditRails continuously runs background verification jobs that re-compute and compare hashes. Any discrepancy triggers an immediate alert.

```
Event 1:  hash_1 = SHA-256(genesis_hash  + payload_1 + ts_1)
Event 2:  hash_2 = SHA-256(hash_1        + payload_2 + ts_2)
Event 3:  hash_3 = SHA-256(hash_2        + payload_3 + ts_3)
              ↑
    Change payload_2 here → hash_2 changes → hash_3 breaks → chain invalid
```

<Warning>
  Each tenant's hash chain is completely independent. Events from different organizations never share a chain, so one tenant's data can never affect another tenant's integrity proof.
</Warning>

***

## Storage Tiers

AuditRails writes every event to two independent storage backends simultaneously:

| Tier     | Backend                          | Purpose                                                                                          |
| -------- | -------------------------------- | ------------------------------------------------------------------------------------------------ |
| **Hot**  | ClickHouse (columnar database)   | Real-time search, dashboard queries, API filtering — sub-second response times                   |
| **Cold** | S3 Object Lock (COMPLIANCE mode) | Long-term WORM retention — objects cannot be deleted or modified by anyone, including AuditRails |

**WORM** (Write Once, Read Many) means that once an event is committed to cold storage, no user action, no admin command, and no AWS API call can remove or overwrite it before the retention period expires. This is enforced at the S3 infrastructure level, not just by software policy.

***

## Retention

Every plan includes the same base retention, applied identically to both storage tiers, and begins from the event's ingest timestamp:

|                            | Hot Storage | Cold Storage (WORM) |
| -------------------------- | ----------- | ------------------- |
| Base retention (all plans) | 180 days    | 180 days            |

Enabling a compliance framework with a longer minimum retention requirement extends this automatically — the longest applicable period always wins. For example, enabling **SOX** raises effective retention to **7 years**; **HIPAA** sets a **6-year** minimum; **NIS2** and **DORA** both require **5 years**.

<Info>
  Upgrading your plan does **not** retroactively extend the retention of existing events. Events already written are retained for the period that was active when they were ingested. New events pick up the new retention period immediately.
</Info>

***

## API Keys

API keys authenticate your requests and scope them to a specific project. AuditRails uses two key formats:

| Format        | Type       | Rate Limit               | Counts Against Plan? |
| ------------- | ---------- | ------------------------ | -------------------- |
| `at_live_xxx` | Production | 1,000 req/s (default)    | Yes                  |
| `at_test_xxx` | Testing    | 60 req/s (separate pool) | No                   |

<Warning>
  Never use `at_test_` keys in production or under real load. They are throttled to 60 req/s and do not guarantee the same durability or retention as live keys.
</Warning>

Keys are **SHA-256 hashed** before being stored in AuditRails' database. The raw key value is never persisted — if you lose it, you must revoke it and generate a new one. Each key is scoped to a single project and carries no cross-project permissions.
