# Changelog — `api`

All notable changes to the **api** container are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and the container adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

The `api` and `ui` containers are versioned independently. The HTTP wire
contract (endpoints, token kinds, RBAC roles, response envelope) is the
boundary between them — bumping the **major** here implies a breaking
change to that contract that consumers must adapt to.

Tags use the `api-v<MAJOR>.<MINOR>.<PATCH>` form so they don't collide
with the UI's tags in this monorepo.

## [Unreleased]

### Added
- Public-endpoint audit emission: `POST /api/v1/report` writes a `report.received` entry attributed to the reporter, and `GET /api/v1/blocklist` writes a `blocklist.requested` entry (including 304s) attributed to the consumer.
- `app_settings` key/value table plus `GET/PATCH /api/v1/admin/app-settings` (admin-only) exposing the two audit toggles (`audit_report_received_enabled`, `audit_blocklist_request_enabled`) so the high-volume rows can be silenced at runtime without a container restart.
- Per-entity `audit_enabled` boolean on `reporters` and `consumers` (default true) editable via the admin PATCH endpoints. Audit emits only when both the global toggle and the entity-level flag are true (AND, not OR).
- `DELETE /api/v1/admin/tokens/{id}/purge` — hard-deletes a previously revoked, non-service token row. Returns 409 on still-active tokens.
- New audit actions: `report.received`, `blocklist.requested`, `app_settings.updated`, `token.deleted`.

### Changed
- Dashboard `/api/v1/admin/stats/dashboard` replaces the single-series `bans_by_day_7d` (manual-block creations per day) with `blocked_ips_by_day_7d`, a per-category time series of distinct IPs reported per UTC day. Shape is `{days: string[], series: [{category, counts}]}`. Categories with zero activity in the window still appear as flat-zero series so the legend stays stable.
- `/api/v1/admin/audit-log` now accepts `subject_kind` + `subject_id` query parameters (must be supplied together; otherwise 400). When set, the row matches if the (kind, id) pair matches *either* the audit row's target *or* its actor — so per-entity detail pages can list both admin actions on the entity and events the entity emitted (`report.received`, `blocklist.requested`).

## [1.0.0] — 2026-05-01

First stable release. Implements every milestone of `SPEC.md` from the
api side (M1–M7, M11–M14) plus shared concerns from M12.

### Added
- Slim 4 + FrankenPHP JSON backend on `:8081` with healthcheck.
- Doctrine DBAL data layer supporting **SQLite** (default) and **MySQL 8 / MariaDB 10.6+**, selected via `DB_DRIVER`. WAL + tuned PRAGMAs on SQLite, transactional writes throughout.
- Phinx migrations and idempotent seeders for every table in `SPEC.md` §4: `reporters`, `consumers`, `api_tokens`, `categories`, `reports`, `ip_scores`, `job_locks`, `job_runs`, `ip_enrichment`, `manual_blocks`, `allowlist`, `policies`, `policy_category_thresholds`, `users`, `oidc_role_mappings`, `audit_log`.
- Four-kind token model (`reporter`, `consumer`, `admin`, `service`) with SHA-256 hashing, `irdb_<kind>_<32 base32>` formatting, and a `RbacMiddleware` driving role enforcement on every admin endpoint.
- `X-Acting-User-Id` impersonation header — only honoured in combination with a `service` token; ignored on every other kind.
- **Public API**: `POST /api/v1/report` (token-bucket rate limiter, 60 req/s/token by default); `GET /api/v1/blocklist` (text + JSON, ETag, 30 s per-consumer cache).
- **Admin API**: full CRUD for reporters, consumers, tokens, categories, policies; manual blocks and allowlist (Operator+ for writes); IP search + IP detail with timeline; dashboard stats; effective-config and jobs-status endpoints; audit-log search; manual job triggers; demo-data seed and operational `purge`.
- **Auth API**: `upsert-oidc`, `upsert-local`, `users/{id}` — service-token-only, used by the UI BFF to translate browser identities into stable user records and roles.
- **Internal jobs**: `recompute-scores`, `cleanup-audit`, `cleanup-expired-manual-blocks`, `enrich-pending`, `refresh-geoip`, `tick`, `status`. Loopback / RFC1918 only via `InternalNetworkMiddleware`; bearer-gated by `INTERNAL_JOB_TOKEN`. Single-shot lock acquisition via `job_locks` with crash-tolerant expiry and `job_runs` history per execution.
- **Reputation engine**: linear and exponential decay, weighted by per-reporter trust at report time. 365-day hard cutoff. Manual blocks and allowlist evaluated at distribution time, not folded into scores; allowlist always wins.
- **Enrichment**: MaxMind GeoLite2-Country/ASN and IPinfo adapters, downloaded at build or refreshed via job; missing-DB scenarios degrade cleanly.
- **Audit log**: every write through admin/auth endpoints emits an entry attributed to the acting user (not the service token), with field-level before/after diffs on updates and human-readable entity labels frozen at write time.
- **Hardening**: security headers (CSP, HSTS, X-Frame-Options, Referrer-Policy), token entropy verified, secrets scrubbed from logs.
- **Documentation**: OpenAPI 3.0.3 served at `/api/v1/openapi.yaml` with viewer at `/api/docs`; canonical reference for request/response schemas.
- CLI `bin/console`: `db:migrate`, `db:rollback`, `db:seed`, `auth:bootstrap-service-token`, `auth:create-token`, `jobs:run`, `jobs:status`, `scores:rebuild`.

[1.0.0]: https://github.com/your-org/irdb/releases/tag/api-v1.0.0
