# Observations API (v1) Status: stable; aligns with LNM v1 (frozen 2025-11-17). ## Intent - Provide raw observation retrieval for graph overlays, audit trails, and detailed provenance inspection. - Observations are the immutable evidence units that feed into linkset aggregation. - Each observation represents a single upstream source's statement about an advisory at a point in time. ## Endpoints ### Query Observations - Method: `GET` - Path: `/advisories/observations` ### Get Observations for Advisory - Method: `GET` - Path: `/concelier/observations` ## Headers | Header | Required | Description | |--------|----------|-------------| | `X-Stella-Tenant` | Yes | Tenant identifier for multi-tenant isolation. | | `X-Stella-Request-Id` | No | Optional correlation ID for distributed tracing. | ## Query Parameters | Parameter | Type | Description | |-----------|------|-------------| | `advisoryKey` | string | Filter by advisory key (CVE, GHSA, etc.). | | `purl` | string[] | Filter by Package URLs (repeatable). | | `source` | string | Filter by upstream source. | | `format` | string | Filter by content format (OSV, GHSA, etc.). | | `limit` | integer | Maximum observations to return (default: 50, max: 200). | | `cursor` | string | Opaque cursor for pagination. | ## Response (200) ```json { "observations": [ { "id": "obs:nvd:CVE-2024-1234:2025-11-20T10:30:00Z", "tenant": "acme", "advisoryKey": "CVE-2024-1234", "aliases": ["CVE-2024-1234", "GHSA-abcd-efgh-ijkl"], "source": "nvd", "format": "OSV", "purls": ["pkg:npm/vulnerable-package@1.0.0"], "cpes": ["cpe:2.3:a:example:vulnerable-package:1.0.0:*:*:*:*:node.js:*:*"], "severity": { "type": "CVSS_V3", "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "baseScore": 9.8, "label": "critical" }, "summary": "Remote code execution vulnerability in vulnerable-package", "publishedAt": "2024-06-15T12:00:00Z", "modifiedAt": "2024-06-20T08:00:00Z", "observedAt": "2025-11-20T10:30:00Z", "provenance": { "connectorId": "nvd-osv-connector", "retrievedAt": "2025-11-20T10:30:00Z", "contentHash": "sha256:a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456", "signaturePresent": false, "signatureVerified": false }, "raw": { "id": "CVE-2024-1234", "modified": "2024-06-20T08:00:00Z", "published": "2024-06-15T12:00:00Z", "aliases": ["CVE-2024-1234"], "summary": "Remote code execution vulnerability in vulnerable-package", "details": "A critical vulnerability exists in vulnerable-package versions prior to 2.0.0...", "severity": [ { "type": "CVSS_V3", "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" } ], "affected": [ { "package": { "ecosystem": "npm", "name": "vulnerable-package" }, "ranges": [ { "type": "SEMVER", "events": [ {"introduced": "0"}, {"fixed": "2.0.0"} ] } ] } ], "references": [ { "type": "ADVISORY", "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234" } ] } }, { "id": "obs:github:GHSA-abcd-efgh-ijkl:2025-11-18T14:00:00Z", "tenant": "acme", "advisoryKey": "CVE-2024-1234", "aliases": ["CVE-2024-1234", "GHSA-abcd-efgh-ijkl"], "source": "github", "format": "GHSA", "purls": ["pkg:npm/vulnerable-package@1.0.0"], "cpes": [], "severity": { "type": "CVSS_V3", "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", "baseScore": 9.8, "label": "critical" }, "summary": "Critical RCE in vulnerable-package", "publishedAt": "2024-06-15T14:00:00Z", "modifiedAt": "2024-06-18T10:00:00Z", "observedAt": "2025-11-18T14:00:00Z", "provenance": { "connectorId": "github-advisory-connector", "retrievedAt": "2025-11-18T14:00:00Z", "contentHash": "sha256:b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456ab", "signaturePresent": true, "signatureVerified": true }, "raw": { "id": "GHSA-abcd-efgh-ijkl", "aliases": ["CVE-2024-1234"], "summary": "Critical RCE in vulnerable-package", "severity": [{"type": "CVSS_V3", "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}], "database_specific": { "github_reviewed": true, "github_reviewed_at": "2024-06-15T14:00:00Z" } } } ], "linkset": { "aliases": ["CVE-2024-1234", "GHSA-abcd-efgh-ijkl"], "purls": ["pkg:npm/vulnerable-package@1.0.0"], "cpes": ["cpe:2.3:a:example:vulnerable-package:1.0.0:*:*:*:*:node.js:*:*"], "references": [ {"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234", "type": "ADVISORY"}, {"url": "https://github.com/advisories/GHSA-abcd-efgh-ijkl", "type": "ADVISORY"} ], "scopes": ["npm"], "relationships": [], "confidence": 0.95, "conflicts": [] }, "nextCursor": null, "hasMore": false, "freshness": null } ``` ## Response with Conflicts When observations from different sources disagree: ```json { "observations": [ { "id": "obs:nvd:CVE-2024-5678:2025-11-20T10:30:00Z", "advisoryKey": "CVE-2024-5678", "source": "nvd", "severity": { "type": "CVSS_V3", "baseScore": 9.8, "label": "critical" }, "observedAt": "2025-11-20T10:30:00Z" }, { "id": "obs:vendor:CVE-2024-5678:2025-11-22T08:00:00Z", "advisoryKey": "CVE-2024-5678", "source": "vendor-security", "severity": { "type": "CVSS_V3", "baseScore": 7.5, "label": "high" }, "observedAt": "2025-11-22T08:00:00Z" } ], "linkset": { "aliases": ["CVE-2024-5678"], "purls": ["pkg:npm/another-package@3.0.0"], "cpes": [], "confidence": 0.72, "conflicts": [ { "field": "severity", "code": "severity-mismatch", "observedValues": [ {"source": "nvd", "value": "critical", "observedAt": "2025-11-20T10:30:00Z"}, {"source": "vendor-security", "value": "high", "observedAt": "2025-11-22T08:00:00Z"} ], "reason": "Sources disagree on severity classification: nvd reports critical (9.8), vendor-security reports high (7.5)" } ] }, "nextCursor": null, "hasMore": false } ``` ## Errors | Status | Code | Description | |--------|------|-------------| | 400 | `ERR_VALIDATION_FAILED` | Invalid query parameters. | | 400 | `ERR_INVALID_CURSOR` | Malformed or expired cursor. | | 401 | `ERR_UNAUTHORIZED` | Missing or invalid authentication. | | 403 | `ERR_FORBIDDEN` | Tenant access denied. | | 404 | `ERR_RESOURCE_NOT_FOUND` | No observations found for advisory. | ### Error Response Example ```json { "type": "https://stellaops.io/errors/validation-failed", "title": "Validation Failed", "status": 400, "detail": "The 'advisoryKey' parameter is required when 'source' is specified.", "instance": "/advisories/observations", "traceId": "trace-id-xyz789", "error": { "code": "ERR_VALIDATION_FAILED", "message": "Missing required parameter.", "target": "advisoryKey", "innerErrors": [ { "field": "advisoryKey", "code": "REQUIRED_WHEN", "message": "advisoryKey is required when source is specified" } ] } } ``` ## Observation Lifecycle 1. **Ingested**: Raw advisory data retrieved from upstream source. 2. **Validated**: Schema validated against content format (OSV, GHSA, etc.). 3. **Stored**: Immutable observation record created with provenance. 4. **Linked**: Observation contributes to linkset aggregation. ## Determinism & Ordering - Observations are ordered by `observedAt desc`, then `source asc`, then `id asc`. - The same query with identical parameters returns identical results. - Cursor-based pagination ensures stable iteration even as new data arrives. ## Notes - Observations are immutable; updates from upstream create new observation records. - The `raw` field contains the unmodified upstream content. - Provenance includes connector identity and content hashes for audit. - Multiple observations may exist for the same advisory from different sources. - Conflicts are detected and surfaced in the aggregate linkset, not resolved. ## Changelog - 2025-12-06: Added curated examples with conflict scenarios (CONCELIER-WEB-OAS-62-001). - 2025-11-17: LNM v1 specification frozen.