## ADDED Requirements

### Requirement: Audit events are written internally for all significant actions
The system SHALL automatically create an immutable `AuditEvent` record for each of the following actions: `agent.created`, `agent.updated`, `agent.decommissioned`, `agent.suspended`, `agent.reactivated`, `token.issued`, `token.revoked`, `token.introspected`, `credential.generated`, `credential.rotated`, `credential.revoked`, `auth.failed`. No API endpoint SHALL allow external creation, modification, or deletion of audit records.

#### Scenario: Audit event created on agent registration
- **WHEN** a new agent is successfully registered via `POST /agents`
- **THEN** an `AuditEvent` with `action: agent.created`, `outcome: success`, and `metadata` containing `agentType` and `owner` is persisted

#### Scenario: Audit event created on failed authentication
- **WHEN** a `POST /token` request fails due to invalid credentials
- **THEN** an `AuditEvent` with `action: auth.failed`, `outcome: failure`, and `metadata` containing `reason` and `clientId` is persisted

#### Scenario: Audit event created on token issuance
- **WHEN** a token is successfully issued via `POST /token`
- **THEN** an `AuditEvent` with `action: token.issued`, `outcome: success`, and `metadata` containing `scope` and `expiresAt` is persisted

### Requirement: Query the audit log with pagination and filtering
The system SHALL return a paginated list of audit events ordered by `timestamp` descending. The caller SHALL hold a valid Bearer token with `audit:read` scope. Filtering SHALL support `agentId`, `action`, `outcome`, `fromDate`, and `toDate` — all optional, combined with logical AND.

#### Scenario: Successful audit log query
- **WHEN** a GET request to `/audit` is received with a valid Bearer token with `audit:read` scope
- **THEN** the system returns `200 OK` with a `PaginatedAuditEventsResponse` containing `data`, `total`, `page`, and `limit`

#### Scenario: Filter by agentId
- **WHEN** a GET request to `/audit?agentId={uuid}` is received
- **THEN** only events where `agentId` equals the provided UUID are returned

#### Scenario: Filter by action
- **WHEN** a GET request to `/audit?action=token.issued` is received
- **THEN** only events with `action: token.issued` are returned

#### Scenario: Filter by date range
- **WHEN** a GET request to `/audit?fromDate=2026-03-01T00:00:00.000Z&toDate=2026-03-28T23:59:59.999Z` is received
- **THEN** only events with `timestamp` within the specified range are returned

#### Scenario: fromDate after toDate rejected
- **WHEN** a GET request to `/audit` is received with `fromDate` that is chronologically after `toDate`
- **THEN** the system returns `400 Bad Request` with `code: VALIDATION_ERROR` and `details.reason` explaining the invalid date range

#### Scenario: Insufficient scope rejected
- **WHEN** a GET request to `/audit` is received with a valid Bearer token that does not have `audit:read` scope
- **THEN** the system returns `403 Forbidden` with `code: INSUFFICIENT_SCOPE`

### Requirement: Retrieve a single audit event by ID
The system SHALL return a single immutable `AuditEvent` by its `eventId`. The caller SHALL hold a valid Bearer token with `audit:read` scope.

#### Scenario: Audit event found
- **WHEN** a GET request to `/audit/{eventId}` is received with a valid Bearer token with `audit:read` scope and a UUID that exists in the audit log
- **THEN** the system returns `200 OK` with the full `AuditEvent` object

#### Scenario: Audit event not found
- **WHEN** a GET request to `/audit/{eventId}` is received with a UUID that does not exist in the audit log
- **THEN** the system returns `404 Not Found` with `code: AUDIT_EVENT_NOT_FOUND`

### Requirement: Free-tier 90-day audit log retention
On the free tier, the system SHALL only return audit events from the last 90 days. Events older than 90 days SHALL be treated as not accessible (return empty results for queries, `404` for direct lookups). The system SHALL return a `400` error with `code: RETENTION_WINDOW_EXCEEDED` if a `fromDate` query parameter falls outside the 90-day retention window.

#### Scenario: Query outside retention window rejected
- **WHEN** a GET request to `/audit` is received with `fromDate` more than 90 days before today
- **THEN** the system returns `400 Bad Request` with `code: RETENTION_WINDOW_EXCEEDED` and `details.retentionDays: 90`

#### Scenario: Direct lookup of expired event returns 404
- **WHEN** a GET request to `/audit/{eventId}` is received for an event with a `timestamp` older than 90 days
- **THEN** the system returns `404 Not Found` with `code: AUDIT_EVENT_NOT_FOUND`

### Requirement: Rate limiting on audit endpoints
The system SHALL enforce a rate limit of 100 requests per minute per authenticated client on all audit endpoints.

#### Scenario: Rate limit exceeded on audit endpoint
- **WHEN** a client sends more than 100 requests to any audit endpoint within a 60-second window
- **THEN** the system returns `429 Too Many Requests` with `X-RateLimit-Limit`, `X-RateLimit-Remaining: 0`, and `X-RateLimit-Reset` headers
