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

# Audit Log

> Append-only, best-effort audit trail covering entity mutations and Keeper security decisions.

# Audit Log

Crewship maintains an append-only audit log that records every mutation in the system. The audit log provides a complete trail for security review, compliance, and debugging.

## What Is Audited

The `audit_logs` table currently captures these mutations. Other
entities (crews, missions, credentials) emit journal events but do
not yet have `WriteAuditLog` calls wired into their handlers:

| Category    | Actions                                                                                                 | Source                                                                 |
| ----------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| **Agents**  | `create`, `update`, `delete`                                                                            | `internal/api/agents_create.go`, `agents_update.go`, `agents_query.go` |
| **Backups** | `backup.create`, `backup.restore`, `backup.unlock`, `backup.rotate`, `backup.delete`, `backup.download` | `internal/api/backup.go`, `backup_admin.go`                            |

Keeper security decisions are tracked separately in the
`keeper_requests` table (see below), not in `audit_logs`.

### Keeper Decision Auditing

Keeper security decisions are stored in the `keeper_requests` table with detailed fields:

| Field                 | Description                                                                                       |
| --------------------- | ------------------------------------------------------------------------------------------------- |
| `intent`              | The agent's stated reason for access                                                              |
| `request_type`        | `access` (credential read via `/keeper/request`) or `execute` (command run via `/keeper/execute`) |
| `command`             | Shell command (for execute requests)                                                              |
| `decision`            | `ALLOW`, `DENY`, or `ESCALATE`                                                                    |
| `reason`              | LLM-generated explanation                                                                         |
| `risk_score`          | Numeric risk assessment (1-10)                                                                    |
| `ollama_prompt`       | Full prompt sent to the Keeper LLM                                                                |
| `ollama_raw_response` | Raw LLM response for review                                                                       |

## Audit Log Table Structure

```sql theme={null}
CREATE TABLE audit_logs (
  id          TEXT PRIMARY KEY,
  workspace_id TEXT NOT NULL,
  user_id     TEXT,
  action      TEXT NOT NULL,
  entity_type TEXT NOT NULL,
  entity_id   TEXT,
  metadata    TEXT,          -- JSON object
  ip_address  TEXT,
  user_agent  TEXT,
  created_at  TEXT NOT NULL   -- ISO 8601
);
```

| Column         | Type   | Description                                                                                                         |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `id`           | `TEXT` | Random hex ID (32 chars)                                                                                            |
| `workspace_id` | `TEXT` | Workspace the action occurred in                                                                                    |
| `user_id`      | `TEXT` | User who performed the action (null for system actions)                                                             |
| `action`       | `TEXT` | Action type — CRUD verbs (`create`, `update`, `delete`) for agent mutations, `backup.*` for backup lifecycle events |
| `entity_type`  | `TEXT` | Entity type (AGENT, CREW, MISSION, etc.)                                                                            |
| `entity_id`    | `TEXT` | ID of the affected entity                                                                                           |
| `metadata`     | `TEXT` | JSON object with action-specific details                                                                            |
| `ip_address`   | `TEXT` | Client IP address                                                                                                   |
| `user_agent`   | `TEXT` | Client user agent string                                                                                            |
| `created_at`   | `TEXT` | ISO 8601 timestamp                                                                                                  |

<Warning>
  The audit log is append-only. There is no API or mechanism to delete or modify audit log entries. This ensures an immutable record for security review.
</Warning>

## Querying the Audit Log

### GET /api/v1/audit

**Required role:** OWNER or ADMIN (manage permission)

### Query Parameters

| Parameter     | Type      | Default | Description                                   |
| ------------- | --------- | ------- | --------------------------------------------- |
| `page`        | `integer` | `1`     | Page number (1-based)                         |
| `limit`       | `integer` | `50`    | Entries per page (1-100)                      |
| `action`      | `string`  |         | Filter by action (e.g., `create`, `delete`)   |
| `entity_type` | `string`  |         | Filter by entity type (e.g., `AGENT`, `CREW`) |
| `entity_id`   | `string`  |         | Filter by specific entity ID                  |
| `user_id`     | `string`  |         | Filter by user who performed the action       |
| `date_from`   | `string`  |         | ISO 8601 start date                           |
| `date_to`     | `string`  |         | ISO 8601 end date                             |

### Response

```json theme={null}
{
  "data": [
    {
      "id": "a1b2c3d4...",
      "workspace_id": "ws-uuid",
      "user_id": "user-uuid",
      "action": "create",
      "entity_type": "AGENT",
      "entity_id": "agent-uuid",
      "metadata": "{\"name\":\"Viktor\",\"role\":\"AGENT\"}",
      "ip_address": "192.168.1.100",
      "user_agent": "Mozilla/5.0...",
      "created_at": "2025-01-15T10:30:00Z",
      "user_email": "alice@example.com",
      "user_name": "Alice Chen"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 234,
    "total_pages": 5
  }
}
```

The response includes `user_email` and `user_name` joined from the users table for convenience.

### Pagination

The audit log uses offset-based pagination:

* **Default limit:** 50 entries per page
* **Maximum limit:** 100 entries per page
* **Minimum limit:** 1 entry per page
* Pages are 1-based (page 1 is the first page)

### Example Queries

<Tabs>
  <Tab title="All agent deletions">
    ```
    GET /api/v1/audit?action=delete&entity_type=AGENT
    ```
  </Tab>

  <Tab title="Actions by a specific user">
    ```
    GET /api/v1/audit?user_id=user-uuid
    ```
  </Tab>

  <Tab title="Recent activity (date range)">
    ```
    GET /api/v1/audit?date_from=2025-01-01T00:00:00Z&date_to=2025-01-31T23:59:59Z
    ```
  </Tab>
</Tabs>

## How Audit Entries Are Written

Audit entries are written synchronously during mutation handlers using the `WriteAuditLog` function (defined at `internal/api/internal_handler.go:51`):

```go theme={null}
func WriteAuditLog(
    ctx context.Context,
    db *sql.DB,
    j journal.Emitter,
    action, entityType, entityID, userID, workspaceID string,
    metadata map[string]interface{},
)
```

Typical call site:

```go theme={null}
WriteAuditLog(r.Context(), h.db, h.journal, "create", "AGENT", agentID, userID, workspaceID, map[string]interface{}{
    "name": agent.Name,
    "role": agent.Role,
})
```

The function generates a random hex ID, writes the row in the same request context, and — when `j` is non-nil — dual-emits a typed `audit.entity_*` entry into the unified Crew Journal. If the write fails, a warning is logged but the original operation is not rolled back -- the audit log is best-effort to avoid blocking user operations.

## What's Next

<CardGroup cols={2}>
  <Card title="Admin API" icon="shield-halved" href="/api-reference/admin">
    Keeper audit log endpoint and workspace administration.
  </Card>

  <Card title="RBAC" icon="users-gear" href="/security/rbac">
    Role-based access control that determines who can view audit logs.
  </Card>
</CardGroup>
