# M13 — Polish, OpenAPI, Documentation > Fresh Claude Code agent prompt. M12 must be complete and committed. > Estimated effort: large. Documentation is a real deliverable; budget time for accuracy. ## Mission Generate `openapi.yaml` and serve it at `/api/v1/openapi.yaml` plus a viewer at `/api/docs`. Write the README with quickstart and operational guides. **Write every `doc/*.md` file as specified in `SPEC.md` §16.** Ship sample reporter scripts and firewall consumer configs in `examples/`. By the end, a fresh clone goes from `git clone` to a working blocklist via documented steps in under 10 minutes. ## Before you start 1. Verify M12: ```bash git log --oneline -12 cd api && composer test && cd .. cd ui && composer test && cd .. ``` 2. Read **`SPEC.md` §16 in full**. The required outline of each `doc/*.md` is the contract for this milestone. Outline-skipping is a hard fail. 3. Skim `SPEC.md` §3 (architecture, for the Mermaid diagram you'll embed in `doc/architecture.md`), §6 (API surface for OpenAPI), §8 (auth flows for `doc/auth-flows.md`). ## Tasks ### 1. OpenAPI spec Place at `api/public/openapi.yaml`. Generate, don't hand-write — but also don't pull in a heavy framework. Two acceptable approaches: - **Annotation-based**: use `zircote/swagger-php` to generate from PHP attributes on the controllers. Add the dep, sprinkle attributes, run `vendor/bin/openapi src -o public/openapi.yaml` as a composer script. CI runs this and fails if the result doesn't match what's committed. - **Hand-curated**: a single `api/openapi.yaml.template.php` file that produces the YAML from PHP code (a structured array you serialize). Simpler; less boilerplate. Pick one and document the choice in PROGRESS.md. Coverage: - All Public endpoints (`/api/v1/report`, `/api/v1/blocklist`). - All Admin endpoints (`/api/v1/admin/*`). - All Auth endpoints with `x-internal: true` extension and a clear "UI BFF only" description. - **Do not** document `/internal/jobs/*` — those are private. Mention in the spec description that they exist but are out of scope for the public contract. Schemas: define all request/response shapes (`Report`, `Blocklist`, `Token`, `Policy`, `User`, `AuditEntry`, error envelopes). Reuse via `$ref`. ### 2. OpenAPI viewer Add a tiny route in `api/src/Application/Public/DocsController.php`: - `GET /api/v1/openapi.yaml` — serves the YAML file with `Content-Type: application/yaml`. Public; no auth. - `GET /api/docs` — serves an HTML page that loads Stoplight Elements or RapiDoc from a CDN-vendored npm static asset (no external CDN). Both are single-script viewers. Pick one (RapiDoc is smaller; Stoplight Elements is prettier). Document the choice. ### 3. README Replace the M01 stub. New contents (in order): 1. **One-paragraph elevator pitch** — what IRDB is, who it's for. 2. **Quickstart** — the 5-minute path: ``` git clone ... cp .env.example .env # edit .env: generate secrets, optionally configure OIDC docker compose -f docker-compose.yml -f compose.scheduler.yml up -d # browse to http://localhost:8080, log in ``` 3. **Generating secrets** — the exact `openssl rand` and `php password_hash` commands. 4. **First-time setup** — create a reporter, get a token, send a report; create a consumer, get a token, fetch the blocklist. 5. **Reverse proxy setup** — point to `examples/reverse-proxy/Caddyfile`. 6. **MySQL setup** — uncomment the section in compose; set `DB_DRIVER=mysql`. 7. **OIDC setup** — point to `doc/oidc.md`. 8. **Scheduling** — host cron, systemd timer, sidecar overlay; point to `examples/scheduler/`. 9. **Backups** — what to back up: the `irdb-data` volume (or MySQL); how to restore. 10. **Architecture** — point to `doc/architecture.md`. 11. **API contract** — point to `/api/docs` viewer and `doc/api-overview.md`. 12. **Replacing the UI** — for future Vue/native/mobile, point to `doc/frontend-development.md`. 13. **License** — TBD (leave a placeholder). ### 4. doc/ files (the real work) Write each file according to its required outline in `SPEC.md` §16. Quality bar applies: every snippet must run as-is against `docker compose up`; no TODOs; ≤500 lines per file. - `doc/architecture.md` — system overview, container topology (Mermaid), where state lives, stable-vs-replaceable surfaces table, why-this-split rationale. - `doc/api-overview.md` — base URL/versioning, auth summary, endpoint groups, common conventions (envelopes, pagination, ETag, rate limits, IP normalization), worked curl examples for: posting a report, pulling a blocklist (text + JSON), admin search via service-token impersonation, admin search via admin-kind token. Pointer to OpenAPI. - `doc/auth-flows.md` — overview table, sequence diagrams (Mermaid) for: machine reporter, admin token, UI BFF (OIDC + local), Entra setup walkthrough (extract from M08's `doc/oidc.md` and merge), local admin guidance, **future user-token flow sketch** marked NOT IMPLEMENTED, CSRF/sessions/CORS notes. - `doc/frontend-development.md` — the headline doc. Read-this-first; three integration patterns (BFF replacement, SPA + thin BFF, direct API + future user tokens) with worked pseudocode for the BFF replacement; minimum API surface checklist; CORS configuration; local dev (run only api, point a separate frontend dev server at it); migration path for swapping UIs at runtime; **what NOT to do** list (no business logic in frontend, no service token in browser, etc.). - `doc/api-reference.md` — short. Pointer to OpenAPI as canonical; documents what OpenAPI doesn't cleanly express: rate-limit headers, ETag semantics, the `X-Acting-User-Id` impersonation header convention, response envelope conventions for current and future batched endpoints. If `doc/oidc.md` was created in M08, **delete it** — its content goes into `doc/auth-flows.md` per `SPEC.md` §16. ### 5. Examples In `examples/`: - `reporters/curl.sh` — a copy-paste shell script: takes an IP and category as args, posts a report. Reads `IRDB_URL` and `IRDB_TOKEN` from env. - `reporters/python.py` — same in Python. Single file, no deps beyond `urllib`. Example of a fail2ban-action wrapper inline as a comment. - `reporters/bash-fail2ban.sh` — drop-in fail2ban action. - `consumers/iptables-restore.sh` — pulls the blocklist, builds an `ipset`, atomic-replace via `ipset restore`. - `consumers/nginx-deny-include.sh` — pulls and writes an `include` file with `deny` directives, reloads nginx. - `consumers/haproxy-acl.sh` — pulls and updates an HAProxy ACL file. - `scheduler/host.crontab`, `scheduler/irdb-tick.service`, `scheduler/irdb-tick.timer` — already stubbed in M01, fill in real content. - `reverse-proxy/Caddyfile` — production-ready Caddy config fronting api and ui. Each script has a header comment explaining usage. Each is shell-checked (run shellcheck) and tested at least manually. ### 6. End-to-end demo test Add `tests/e2e/demo.sh` (and a CI job that runs it) that automates the README quickstart: 1. `docker compose -f ... up -d` 2. Generate admin token via CLI 3. Create reporter + consumer + tokens 4. Submit reports 5. Trigger recompute job 6. Pull blocklist; assert non-empty 7. `docker compose down -v` ### 7. Doc accuracy CI check Add a CI job that: - Greps `doc/*.md` for endpoint paths; compares against the OpenAPI document. Any path in docs not in the spec → fail. (The other direction is OK; not every endpoint needs prose.) - Greps for token kind strings (`reporter`, `consumer`, `admin`, `service`); ensures spelling matches code. - Optional: dead-link checker on the docs. ## Implementation notes - **Mermaid in GitHub-rendered Markdown**: use `mermaid` fenced code blocks. Test by rendering the file on a GitHub PR. - **Pseudocode in frontend-development.md**: keep it language-neutral or use a minimal Node/Express snippet that's clearly a sketch, not a runnable thing. The point is the pattern, not a working app. - **Avoid screenshots**: explicitly forbidden by SPEC §16 quality bar — they go stale. - **OpenAPI versioning**: declare `version: 1.0.0` in the spec; bump for breaking changes. Document the additive-only policy. - **Don't describe what doesn't exist**: every claim in docs must match the as-built code. If you find a discrepancy mid-writing, **fix the code**, not the docs. ## Out of scope (DO NOT) - Marketing site / landing page. README is enough. - Versioned docs site (Docusaurus, MkDocs). The Markdown files in `doc/` are the docs. - Auto-generating client SDKs. Future work. - Tutorials beyond the quickstart in README. The doc files are reference, not tutorials. - Adding new endpoints. If something feels needed for docs that isn't in the code, stop and reconsider. - New dependencies beyond `zircote/swagger-php` (if you go that route). Record in PROGRESS.md. ## Acceptance ```bash cd api && composer cs && composer stan && composer test && cd .. cd ui && composer cs && composer stan && composer test && cd .. # OpenAPI is valid docker run --rm -v "$(pwd)/api/public:/spec" \ redocly/cli:latest lint /spec/openapi.yaml # Doc files exist and are non-empty for f in architecture api-overview auth-flows frontend-development api-reference; do test -s "doc/$f.md" || { echo "missing or empty: doc/$f.md"; exit 1; } done # Each doc file is ≤500 lines for f in doc/*.md; do L=$(wc -l < "$f"); [ "$L" -le 500 ] || { echo "$f too long: $L lines"; exit 1; } done # Doc accuracy: no stale endpoints # (run the CI check script you wrote) ./scripts/check-doc-endpoints.sh # E2E demo script docker compose down -v ./tests/e2e/demo.sh docker compose down -v # /api/docs serves a viewer docker compose up -d sleep 15 curl -sf http://localhost:8081/api/v1/openapi.yaml | grep -q "openapi:" curl -sf http://localhost:8081/api/docs | grep -qE "(rapi-doc|stoplight|elements)" docker compose down -v # Examples are shellcheck-clean shellcheck examples/reporters/*.sh examples/consumers/*.sh examples/scheduler/host.crontab 2>/dev/null || true ``` ## Handoff 1. Commit: ``` feat(M13): polish — OpenAPI, README, doc/, examples, e2e demo - openapi.yaml served at /api/v1/openapi.yaml; /api/docs viewer - README with quickstart, OIDC pointer, scheduler options, backups - doc/{architecture,api-overview,auth-flows,frontend-development,api-reference}.md - examples/{reporters,consumers,scheduler,reverse-proxy} with shell-checked scripts - tests/e2e/demo.sh: clone-to-blocklist in ~10 minutes - CI: openapi validation, doc-endpoint accuracy check ``` 2. Append to `PROGRESS.md`: ```markdown ## M13 — Polish, OpenAPI, docs (done) **Built:** OpenAPI + viewer; README; all five doc files per SPEC §16; examples; e2e test. **Notes for next milestone:** - OpenAPI generation is via [zircote/swagger-php OR hand-curated array]; the source is ``. - Doc CI guard: ./scripts/check-doc-endpoints.sh - examples/ scripts use IRDB_URL and IRDB_TOKEN env vars; document this convention. - The "future user-token flow" in doc/auth-flows.md is the recommended extension point for SPA/native/mobile UIs. **Deviations from SPEC:** none. **Added dependencies:** [zircote/swagger-php if applicable] ``` 3. **Stop.** Do not start M14.