Commit ae65658e by PLN (Algolia)

docs(tasks): archive #48 — docs/openapi gated behind api:docs (deployed live)

Rich entry for the documentary: the anonymous-exposure finding, the cross-cutting
api:docs design (read-specs decoupled from call-API), the lockstep drift-guard
dance, and the shared-repo rebase + diff-before-tee discipline that kept SRE work
intact.
parent d4d27a49
...@@ -368,3 +368,9 @@ memory `project_fourier_api`. Named after Joseph Fourier — the transform behin ...@@ -368,3 +368,9 @@ memory `project_fourier_api`. Named after Joseph Fourier — the transform behin
- **Done:** committed c7e5392. `engines/sources.py` registers the `sources` job handler: yt-dlp → `bestaudio` → 44.1k WAV (PLN's choice: analysis-ready PCM over keep-source-codec), content-address, store as a `source` artifact, `put_alias(url→cid)`. `POST /sources` returns `202 {job_id}`; a re-fetched URL (alias hit + artifact still on disk) is a **born-`done` job** — no re-download. `normalize_url()` validates: http(s) only, lowercases host, drops fragment (so trivially-different URLs dedupe), and hard-blocks SSRF footguns (localhost, RFC1918/loopback/link-local/reserved IP literals, incl. the 169.254.169.254 cloud-metadata classic). The fetch is a thin seam (`_fetch_audio_wav`) so tests mock the network with a synthetic WAV — 17 new tests (78→95), zero network. healthz reports yt-dlp presence honestly. - **Done:** committed c7e5392. `engines/sources.py` registers the `sources` job handler: yt-dlp → `bestaudio` → 44.1k WAV (PLN's choice: analysis-ready PCM over keep-source-codec), content-address, store as a `source` artifact, `put_alias(url→cid)`. `POST /sources` returns `202 {job_id}`; a re-fetched URL (alias hit + artifact still on disk) is a **born-`done` job** — no re-download. `normalize_url()` validates: http(s) only, lowercases host, drops fragment (so trivially-different URLs dedupe), and hard-blocks SSRF footguns (localhost, RFC1918/loopback/link-local/reserved IP literals, incl. the 169.254.169.254 cloud-metadata classic). The fetch is a thin seam (`_fetch_audio_wav`) so tests mock the network with a synthetic WAV — 17 new tests (78→95), zero network. healthz reports yt-dlp presence honestly.
- **Learnings:** PLN chose OPEN url scope (any http(s)) over an allowlist, explicitly leaning on "our apikey + nechapi monitoring" as the protection layer — so the code accepts any public URL but still refuses the SSRF set in-process (defense the gateway can't do). yt-dlp+ffmpeg are **worker-only** deps (the API container stays torch/yt-dlp-free) → added to requirements-deploy, not requirements-test. The idempotent born-done path falls out of the #25 `enqueue(..., result=...)` fast path + #26 aliases — the URL→cid alias table was designed in #26 *for* exactly this. - **Learnings:** PLN chose OPEN url scope (any http(s)) over an allowlist, explicitly leaning on "our apikey + nechapi monitoring" as the protection layer — so the code accepts any public URL but still refuses the SSRF set in-process (defense the gateway can't do). yt-dlp+ffmpeg are **worker-only** deps (the API container stays torch/yt-dlp-free) → added to requirements-deploy, not requirements-test. The idempotent born-done path falls out of the #25 `enqueue(..., result=...)` fast path + #26 aliases — the URL→cid alias table was designed in #26 *for* exactly this.
- **Deps:** blocked-by #25 (done); unblocks #30 (separate consumes the fetched WAV). Go-live (yt-dlp install on erable) tracked in #47. - **Deps:** blocked-by #25 (done); unblocks #30 (separate consumes the fetched WAV). Go-live (yt-dlp install on erable) tracked in #47.
## #48 — Gate /docs + /openapi.json behind a cross-cutting api:docs scope
- **Description:** PLN noticed `api.nech.pl/docs` "down" + asked if docs should be token-protected. Investigation found two things: (1) root `/docs` is 404 (the platform Scalar portal was never built; the 404 catch-all's "See /docs" hint dangles), and (2) the real issue — audio's `public_routes` exposed `/audio/v1/{docs,openapi.json}` ANONYMOUSLY: the full API surface advertised to the internet.
- **Done:** deployed live 2026-06-29. `required_for()` special-cases `docs|openapi.json|redoc``{realm}:docs` (canonical `nechapi/_platform/scopes.py` commit on branch `claude/docs-scope`, rebased onto upstream + merged to main `0ab148b`; vendored byte-identical into `armada/api/scopes.py`, commit `d4d27a4`). Dropped docs/openapi from `audio.public_routes` (kept `/healthz`), re-rendered the vhost. Rebuilt + shipped `nechapi-platform:latest` (Alpine, Postgres-backed so no token loss), recreated the container on `nechapi-net`, installed the re-rendered vhost (diffed-first: only my 2 blocks removed), `nginx -t && reload`. VERIFIED LIVE: anon /docs+/openapi.json → 401 (was 200), /healthz → 200; a minted `api:docs` token → /docs 200 but POST /analyze/emotion → 403 (decoupling proven); test token revoked. `api:*` (hexa/pln) keeps docs access via subtree.
- **Learnings:** the cross-cutting scope (`api:docs`, not per-API `api:audio:docs`) decouples "read the specs" from "call the API" — a docs-reader token can't invoke anything, and `api:*` still covers both via the subtree wildcard. The drift guard reads the MAIN `~/Work/nechapi` checkout, so the canonical + vendored copies must merge in LOCKSTEP — committing the vendored copy ahead of the canonical merge turns the local suite red (CI skips it when the canonical is absent). Resolved by reverting the premature vendor, staging on a branch, and re-vendoring only after the nechapi merge. Also re-learned the shared-repo race: origin/main had advanced (a parallel SRE `bin/nechapi` drift-guard commit) — rebased onto it (no overlap), and ALWAYS diff the rendered vhost vs the LIVE one before `sudo tee` so a parallel session's work isn't clobbered.
- **Deps:** spun out of the #29/#27 deploy session; touched the security control plane (gated). Follow-up: build the platform `/docs` Scalar portal (basic-auth RTFM/PLN per registry) + fix the 404 "See /docs" dangling hint.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment