# `/advisories/summary` API (draft v1) Status: draft; aligns with LNM v1 (frozen 2025-11-17) and observation/linkset models already shipped in Concelier Core. ## Intent - Provide graph overlays and consoles a deterministic summary of observations and linksets without derived verdicts. - Preserve provenance and tenant isolation; results are stable for a given tenant + filter set. ## Request - Method: `GET` - Path: `/advisories/summary` - Headers: - `X-Stella-Tenant`: required. - `X-Stella-Request-Id`: optional for tracing. - Query parameters: - `purl` (optional, repeatable) — filter by component coordinates. - `alias` (optional, repeatable) — advisory aliases (CVE, GHSA, vendor IDs); case-insensitive; normalized and sorted server-side. - `source` (optional, repeatable) — upstream source identifiers. - `confidence_gte` (optional, decimal 0–1) — minimum linkset confidence. - `conflicts_only` (optional, bool) — when `true`, return only summaries with conflicts present. - `after` (optional, cursor) — opaque, tenant-scoped cursor for pagination. - `take` (optional, int, default 100, max 500) — page size. - `sort` (optional, enum: `advisory`, `observedAt`, default `advisory`) — always ascending and stable. ## Response (200) ```json { "meta": { "tenant": "acme", "count": 2, "next": "opaque-cursor-or-null", "sort": "advisory" }, "items": [ { "advisoryKey": "cve-2024-1234", "aliases": ["GHSA-xxxx-yyyy", "CVE-2024-1234"], "source": "nvd", "observedAt": "2025-11-22T15:04:05Z", "linksetId": "ls_01H9A8...", "confidence": 0.82, "conflicts": [ { "field": "severity", "codes": ["severity-mismatch"], "sources": ["nvd", "vendor"] } ], "counts": { "observations": 3, "conflictFields": 1 }, "provenance": { "observationIds": ["obs_01H9...", "obs_01H9..."], "schema": "lnm-1.0" } } ] } ``` - Ordering: stable by `sort` then `advisoryKey` then `linksetId`. - Pagination: cursor supported when `sort=observedAt`; for `sort=advisory` cursor is currently null (single page per request). - No derived verdicts or merged severity values; conflicts are emitted as structured markers only. ## Errors - `400` `ERR_AOC_001`: missing/invalid tenant or unsupported filter. - `400` `ERR_AOC_006`: `take` exceeds max or invalid cursor. - `401/403`: auth/tenant scope failures. - `429`: if per-tenant rate limits enforced (surface retry headers). ## Determinism & caching - Response depends solely on tenant + normalized filters + underlying linksets; no clock-based variation beyond `observedAt` from stored records. - Cache key (for optional summary cache): - `tenant|purls(sorted)|aliases(sorted)|sources(sorted)|confidence_gte|conflicts_only|sort|take|after` - Return transparency headers: `X-Stella-Cache-Key` (sha256-16), `X-Stella-Cache-Hit`, `X-Stella-Cache-Ttl`. - Cache TTL should default to 0 (disabled) until validated; determinism required for hits. ## Notes - Cursor must encode the final sort tuple (`advisoryKey`, `linksetId`, `observedAt`) to keep pagination stable when new data arrives. - All string comparisons for filters are case-insensitive; server normalizes to lower-case and sorts before query execution. - Conflicts mirror LNM conflict codes already produced by Core (no new codes introduced here).