38 lines
3.1 KiB
Markdown
38 lines
3.1 KiB
Markdown
# Concelier · Policy Engine Linkset API Prep
|
||
|
||
- **Date:** 2025-11-20
|
||
- **Scope:** PREP-CONCELIER-POLICY-20-001 (LNM APIs not exposed via OpenAPI)
|
||
- **Working directory:** `src/Concelier/StellaOps.Concelier.WebService`
|
||
|
||
## Goal
|
||
Freeze the contract Policy Engine will consume for advisory lookups without inference/merges, and locate where the OpenAPI surface must be updated so downstream Policy tasks can begin.
|
||
|
||
## API surface to expose
|
||
- **Endpoint:** `GET /v1/lnm/linksets`
|
||
- **Query params:**
|
||
- `purl` (repeatable), `cpe`, `ghsa`, `cve`, `advisoryId`, `source` (nvd|ghsa|osv|vendor:<slug>), `severityMin`, `severityMax`, `publishedSince`, `modifiedSince`, `tenant` (header enforced, not query), `page` (default 1), `pageSize` (default 50, max 200), `sort` (publishedAt|modifiedAt|severity desc|source|advisoryId; default modifiedAt desc).
|
||
- **Response:** deterministic ordering; body fields = `advisoryId`, `source`, `purl[]`, `cpe[]`, `summary`, `publishedAt`, `modifiedAt`, `severity` (source-native), `status` (facts only), `provenance` (`ingestedAt`, `connectorId`, `evidenceHash`, `dsseEnvelopeHash?`), `conflicts[]` (raw disagreements, no merged verdicts), `timeline[]` (raw timestamps + hashes), `remarks[]` (human notes, optional).
|
||
- **Endpoint:** `GET /v1/lnm/linksets/{advisoryId}`
|
||
- Mirrors above fields; adds `normalized` block for any canonicalized IDs; `cached` flag already added in Sprint 110.B endpoint work.
|
||
- **Endpoint:** `POST /v1/lnm/linksets/search`
|
||
- Accepts body with same filters as query params plus boolean `includeTimeline`, `includeObservations` (default false). Must respect tenant guard and AOC (no inferred verdicts or merges).
|
||
|
||
## OpenAPI tasks
|
||
- Source file location: `src/Concelier/StellaOps.Concelier.WebService/openapi/concelier-lnm.yaml` (to be created / updated alongside code) and published copy under `docs/api/concelier/`.
|
||
- Add components:
|
||
- `LinksetProvenance` object (ingestedAt, connectorId, evidenceHash, dsseEnvelopeHash?).
|
||
- `LinksetConflict` object (source, field, observedValue, observedAt, evidenceHash).
|
||
- `LinksetTimeline` object (event, at, evidenceHash, dsseEnvelopeHash?).
|
||
- Pagination envelope: `{ "items": [...], "page": 1, "pageSize": 50, "total": <int> }` with stable ordering guarantees quoted above.
|
||
- Security: `Tenant` header required; bearer/mtls unchanged from existing WebService.
|
||
|
||
## Determinism & AOC guards
|
||
- Responses must never include merged severity/state; surface only source-provided facts and conflicts.
|
||
- Sorting: primary `modifiedAt desc`, tie-breaker `advisoryId asc`, then `source asc` for deterministic pagination.
|
||
- Cache: the `/linksets/{advisoryId}` endpoint may serve cached entries but must include `cached: true|false` and `provenance.evidenceHash` so Policy Engine can verify integrity.
|
||
|
||
## Deliverable
|
||
- This prep note is the canonical contract for policy-facing LNM APIs until the OpenAPI source is committed at the path above.
|
||
- Downstream tasks (POLICY-ENGINE-20-001 and linked Policy Engine sprints) should bind to these fields; any deviations must update this prep note and the sprint’s Decisions & Risks.
|
||
|