# Audit Log Manager — Admin Planning Document

This plan describes the **Audit Log Manager**: an admin-only surface for tracing and monitoring **per-user activity** in the web application, with **time** as the primary navigation axis. It is derived from and must stay consistent with [`assets/docs/audit-log-implementation.md`](../assets/docs/audit-log-implementation.md) (schema, `record_audit`, role-based logging rules, and Step 6 admin viewer).

---

## 1. Goals

| Goal | Description |
|------|-------------|
| **Forensic traceability** | Answer: “What did user *U* do, and when?” and “What happened to entity *E* between *T₁* and *T₂*?” |
| **Time-first exploration** | Default and sort order anchored on **`occurred_at`** (UTC stored as `TIMESTAMPTZ`; display in admin’s locale or a configured org timezone). |
| **Tamper-aware source** | Queries read from append-only `audit_events` (app role: `INSERT`/`SELECT` only per implementation guide). |
| **No scope creep** | v1 is **query + view**, not anomaly detection, not primary shipping to external log stacks (see implementation guide §9). |

---

## 2. Relationship to backend data model

The implementation guide defines a unified **`audit_events`** table keyed by time:

- **`occurred_at`** — canonical **timestamp** for all list, filter, and timeline views; indexed with `actor_user_id` and other dimensions.
- **Actor columns** — `actor_user_id`, `actor_email`, `actor_role` (denormalized at event time).
- **Target** — `action`, `entity_type`, `entity_id`, optional `before` / `after` JSONB snapshots, `metadata`, `request_id`.

**Note:** The repository may also contain legacy `audit_log` / trigger-based patterns. The Audit Log Manager should target the **new** `audit_events` model from the implementation guide once migrated; until then, scope API field mapping explicitly so the UI contract does not depend on legacy tables.

---

## 3. Admin API design

### 3.1 Endpoint

- **`GET /api/v1/admin/audit`** — admin role check on router **and** service layer (mirror implementation guide §8).

### 3.2 Query parameters (time + filters)

| Parameter | Purpose |
|-----------|---------|
| **`from`** | Inclusive lower bound on `occurred_at` (ISO-8601 / RFC3339, timezone-aware). |
| **`to`** | Exclusive or inclusive upper bound (pick one, document in OpenAPI; default: *inclusive* end-of-day if date-only strings are allowed). |
| **`actor_user_id`** | Filter by user UUID (strong identifier for “this user’s timeline”). |
| **`actor_email`** | Partial or exact match per product choice; prefer **exact** for predictable forensic use. |
| **`entity_type`**, **`entity_id`** | Narrow to one record’s history. |
| **`action`** | One or many `audit_action` values (`create`, `read`, `update`, `delete`, `login`, `logout`, `login_failed`). |
| **`request_id`** | Optional: correlate all events from one HTTP request. |
| **`page`**, **`page_size`** | Paginated results; cap `page_size` (e.g. 100) for performance. |
| **`sort`** | Default **`occurred_at,desc`**; optional `asc` for chronological “playback” within a window. |

### 3.3 Response shape

Each item should expose at minimum:

- `id`, `occurred_at`, `actor_user_id`, `actor_email`, `actor_role`, `action`, `entity_type`, `entity_id`, `metadata`, `request_id`, and flags or URLs indicating presence of `before` / `after` (full JSON may be omitted from list rows and loaded on detail fetch if payloads are large).

Optional follow-up:

- **`GET /api/v1/admin/audit/{id}`** — full row including `before` / `after` for the detail panel.

### 3.4 Query strategy

- Always constrain by **`occurred_at`** when the admin does not pass `from`/`to` — e.g. default **last 24 hours** or **last 7 days** to avoid full table scans.
- Use existing indexes: `(actor_user_id, occurred_at DESC)`, `(entity_type, entity_id, occurred_at DESC)`, `(action, occurred_at DESC)`.

---

## 4. Admin UI — Audit Log Manager

### 4.1 Placement and access

- Route: **`/dashboard/admin/audit`** (or equivalent admin layout path).
- Wrapped in **`ProtectedRoute`** (or equivalent) **admin-only**; no feature flags that downgrade the check in production.

### 4.2 Layout

1. **Filter bar** — time range, user (email or ID), entity type/id, action multiselect, optional free-text on `metadata` paths (defer to v2 unless trivial; GIN on `metadata` supports it later).
2. **Results table** — columns: **Time** (`occurred_at`), **Actor** (email + role), **Action**, **Entity** (type + id), **Summary** (one-line from metadata or action), **Request** (link/filter by `request_id` if present).
3. **Detail drawer / panel** — on row click: show full metadata, **`before` / `after`** side-by-side; **diff in the browser** with a JSON diff library (per implementation guide — do not compute diff on server).

### 4.3 Time-centric UX presets

Presets speed up monitoring:

- Last 24 hours, last 7 days, last 30 days, custom range (date-time pickers with timezone label).
- **“This user’s activity”** deep link: `/dashboard/admin/audit?actor_user_id=…&from=…&to=…`.

### 4.4 Monitoring-oriented views (v1 vs later)

| View | v1 | Later |
|------|----|--------|
| Paginated event list with time filters | Yes | — |
| Sort by `occurred_at` | Yes | — |
| Per-user timeline (same data, fixed `actor_user_id`) | Yes | Optional density / grouping by day |
| Per-entity history | Yes | — |
| Export (CSV/JSON) for compliance | Optional | Regulated customers often need it |
| Live / websocket tail | No | Only if product requires real-time |

---

## 5. Timestamp semantics

- **Storage:** `TIMESTAMPTZ` in PostgreSQL; all APIs use ISO-8601 with offset or `Z`.
- **Display:** Convert to admin-visible timezone; show UTC in tooltip or secondary row for disputes.
- **Day boundaries:** If the UI sends date-only `from`/`to`, define explicitly (e.g. start of day / end of day in chosen timezone, then convert to UTC for the query).

---

## 6. Privacy, security, and correctness

- **Admin-only** on UI and API; verify with automated tests.
- **Do not** log secrets in `before`/`after` (enforce `to_dict()` hygiene per implementation guide §5.1 and §8).
- **Failed login** events: `actor_user_id` null, `actor_email` = attempted address; UI must not imply existence of accounts beyond what policy allows (align with auth UX).
- **Read volume:** Applicant read-audit is mostly suppressed by design; admins see **all** reads they are allowed to see — train admins that “quiet” applicant timelines are expected.

---

## 7. Implementation phases (aligned with implementation guide §7)

| Phase | Deliverable for Audit Log Manager |
|-------|-----------------------------------|
| **After Step 1** | Stub UI + API returns empty or mock; contract freeze. |
| **After Steps 2–4** | Real data for auth, user admin, initiative writes; filters and table usable for forensic queries. |
| **After Step 5** | Read events appear per role rules; admin sees broad read coverage. |
| **Step 6 (shipping target)** | Full **Audit Log Manager** as specified here: filters, pagination, detail panel, time presets. |
| **Step 7** | Retention/partitioning — Manager may add UI notice “data older than X may be purged per policy.” |

---

## 8. Acceptance criteria (Audit Log Manager)

- [ ] Admin cannot access `/api/v1/admin/audit` as non-admin (403).
- [ ] Listing is **ordered by `occurred_at`** descending by default; ascending works for a bounded window.
- [ ] Filtering by **`from` / `to`** reduces rows correctly and uses indexed columns.
- [ ] Filtering by **`actor_user_id`** or **`actor_email`** returns only that actor’s events.
- [ ] Row detail shows **`before` / `after`** without server-side diff; client-side diff renders correctly for large JSON (smoke test with sample initiative snapshot).
- [ ] Time preset “last 7 days” matches server interpretation (document timezone).
- [ ] No secrets appear in exported or displayed JSON (spot-check against `to_dict()` tests).

---

## 9. References

- Primary spec: [`assets/docs/audit-log-implementation.md`](../assets/docs/audit-log-implementation.md) — sections 2 (schema), 4 (`record_audit`), 5 (where writes happen), 6 (what to log per role), 7 Step 6 (admin viewer), 8 (verification).
