# Console Workspaces API _Tracking: CONSOLE-VULN-29-001, CONSOLE-VEX-30-001, DOCS-AIAI-31-004_ ## 1. Goals & Scope The console workspaces provide read-only aggregates for Advisory AI operators: - `/console/vuln/*` surfaces tenant-scoped findings annotated with policy verdicts, VEX justifications, Scheduler reachability signals, and Advisory AI rationale. - `/console/vex/*` streams the underlying VEX statements, conflicts, and justification summaries (with SSE support for live updates). All endpoints MUST: 1. Remain deterministic offline (stable sort keys, ISO-8601 UTC timestamps, hashed assets). 2. Operate with Authority-issued DPoP or mTLS client credentials that include `console:read` and either `vuln:read` or `vex:read`. 3. Respect tenant isolation – every request carries `X-StellaOps-Tenant`. ## 2. Shared Request/Response Conventions | Requirement | Description | | --- | --- | | Headers | `Authorization: DPoP `, `DPoP: `, `X-StellaOps-Tenant: `, `Accept: application/json` (or `text/event-stream` for SSE). | | Pagination | Cursor-based via `pageToken`; defaults to 50 items, max 200. Cursors are opaque, base64url, signed. | | Sorting | Findings sorted by `(severity desc, exploitScore desc, findingId asc)`. Statements sorted by `(lastUpdated desc, statementId asc)`. | | Dates | RFC 3339 / ISO-8601 UTC (e.g., `2025-11-08T12:02:11Z`). | | Determinism | All arrays must be pre-sorted; no server-generated uuids in responses. | ## 3. Vulnerability Workspace (`/console/vuln/*`) ### 3.1 `GET /console/vuln/findings` Query parameters: | Parameter | Type | Notes | | --- | --- | --- | | `pageToken` | string | Optional cursor from previous response. | | `pageSize` | int | 1-200, default 50. | | `severity` | string[] | Accepts `critical`, `high`, `medium`, `low`, `info`. | | `product` | string[] | SBOM `purl` or image digest anchors. | | `policyBadge` | string[] | `pass`, `warn`, `fail`, `waived`. | | `vexState` | string[] | `not_affected`, `fixed`, `under_investigation`, etc. | | `reachability` | string[] | `reachable`, `unreachable`, `unknown`. | | `search` | string | Substring match on CVE/GHSA/KEV ID (case-insensitive). | Response body: ```jsonc { "items": [ { "findingId": "tenant-default:advisory-ai:sha256:5d1a", "coordinates": { "advisoryId": "CVE-2024-12345", "package": "pkg:npm/jsonwebtoken@9.0.2", "component": "jwt-auth-service", "image": "registry.local/ops/auth:2025.10.0" }, "summary": "jsonwebtoken <10.0.0 allows algorithm downgrade.", "severity": "high", "cvss": 8.1, "kev": true, "policyBadge": "fail", "vex": { "statementId": "vex:tenant-default:jwt-auth:5d1a", "state": "under_investigation", "justification": "Advisory AI flagged reachable path via Scheduler run 42." }, "reachability": { "status": "reachable", "lastObserved": "2025-11-07T23:11:04Z", "signalsVersion": "signals-2025.310.1" }, "evidence": { "sbomDigest": "sha256:6c81…", "policyRunId": "policy-run::2025-11-07::ca9f", "attestationId": "dsse://authority/attest/84a2" }, "timestamps": { "firstSeen": "2025-10-31T04:22:18Z", "lastSeen": "2025-11-07T23:16:51Z" } } ], "facets": { "severity": [ { "value": "critical", "count": 2 }, { "value": "high", "count": 7 } ], "policyBadge": [ { "value": "fail", "count": 6 }, { "value": "warn", "count": 3 }, { "value": "waived", "count": 1 } ], "reachability": [ { "value": "reachable", "count": 5 }, { "value": "unreachable", "count": 2 }, { "value": "unknown", "count": 1 } ] }, "nextPageToken": "eyJjdXJzb3IiOiJmZjg0NiJ9" } ``` ### 3.2 `GET /console/vuln/facets` Returns the full facet catalog (counts by severity, product, policy badge, VEX state, reachability, KEV flag). Designed for sidebar filters without paging; identical parameter surface as `/findings`. ### 3.3 `GET /console/vuln/{findingId}` Returns the full finding document, including evidence timeline, policy overlays, and export-ready metadata: ```jsonc { "findingId": "tenant-default:advisory-ai:sha256:5d1a", "details": { "description": "jsonwebtoken <10.0.0 allows algorithm downgrade.", "references": [ "https://nvd.nist.gov/vuln/detail/CVE-2024-12345", "https://github.com/auth0/node-jsonwebtoken/security/advisories/GHSA-45mw-4jw3-g2wg" ], "exploitAvailability": "known_exploit" }, "policyBadges": [ { "policyId": "policy://tenant-default/runtime-hardening", "verdict": "fail", "explainUrl": "https://console.local/policy/runs/policy-run::2025-11-07::ca9f" } ], "vex": { "statementId": "vex:tenant-default:jwt-auth:5d1a", "state": "under_investigation", "justification": "Runtime telemetry confirmed exploitation path.", "impactStatement": "Token exchange service remains exposed until patch 2025.11.2.", "remediations": [ { "type": "patch", "description": "Upgrade jwt-auth-service to 2025.11.2.", "deadline": "2025-11-12T00:00:00Z" } ] }, "reachability": { "status": "reachable", "callPathSamples": [ "api-gateway -> jwt-auth-service -> jsonwebtoken.verify" ], "lastUpdated": "2025-11-07T23:11:04Z" }, "evidence": { "sbom": { "digest": "sha256:6c81…", "componentPath": [ "/src/jwt-auth/package.json", "/src/jwt-auth/node_modules/jsonwebtoken" ] }, "attestations": [ { "type": "scan-report", "attestationId": "dsse://authority/attest/84a2", "signer": "attestor@stella-ops.org", "bundleDigest": "sha256:e2bb…" } ] }, "timestamps": { "firstSeen": "2025-10-31T04:22:18Z", "lastSeen": "2025-11-07T23:16:51Z", "vexLastUpdated": "2025-11-07T23:10:09Z" } } ``` ### 3.4 `POST /console/vuln/tickets` ```jsonc POST /console/vuln/tickets { "tenant": "tenant-default", "selection": [ "tenant-default:advisory-ai:sha256:5d1a", "tenant-default:advisory-ai:sha256:9bf4" ], "targetSystem": "servicenow", "metadata": { "assignmentGroup": "runtime-security", "priority": "P1" } } ``` Response: ```jsonc { "ticketId": "console-ticket::tenant-default::2025-11-08::00018", "payload": { "version": "2025-11-01", "tenant": "tenant-default", "findings": [ { "findingId": "tenant-default:advisory-ai:sha256:5d1a", "severity": "high" }, { "findingId": "tenant-default:advisory-ai:sha256:9bf4", "severity": "critical" } ], "policyBadge": "fail", "vexSummary": "2 reachable findings pending patch.", "attachments": [ { "type": "json", "name": "console-ticket-20251108.json", "digest": "sha256:1fdd…", "contentType": "application/json", "expiresAt": "2025-11-15T00:00:00Z" } ] }, "auditEventId": "console.ticket.export::2025-11-08::00018" } ``` Requests emit `console.ticket.export` audit events (tenant, user, selection counts, target system). ## 4. VEX Workspace (`/console/vex/*`) ### 4.1 `GET /console/vex/statements` Parameters mirror `/console/vuln/findings` plus: | Parameter | Type | Notes | | --- | --- | --- | | `advisoryId` | string[] | CVE/GHSA/OVAL identifiers. | | `justification` | string[] | `exploit_observed`, `component_not_present`, etc. | | `statementType` | string[] | `vex`, `openvex`, `custom`, `advisory_ai`. | | `prefer` | string | `prefer=stream` enables chunked streaming (NDJSON). | Response (paged JSON): ```jsonc { "items": [ { "statementId": "vex:tenant-default:jwt-auth:5d1a", "advisoryId": "CVE-2024-12345", "product": "registry.local/ops/auth:2025.10.0", "status": "under_investigation", "justification": "exploit_observed", "lastUpdated": "2025-11-07T23:10:09Z", "source": { "type": "advisory_ai", "modelBuild": "aiai-console-2025-10-28", "confidence": 0.74 }, "links": [ { "rel": "finding", "href": "/console/vuln/findings/tenant-default:advisory-ai:sha256:5d1a" } ] } ], "nextPageToken": null } ``` When `Accept: text/event-stream`, the endpoint emits events (see §4.3) instead of paged JSON. ### 4.2 `GET /console/vex/statements/{statementId}` Returns the canonical statement plus provenance extracts. SSE clients can call this endpoint when they need full bodies after receiving a summary event. ### 4.3 `GET /console/vex/events` (SSE) Streams live updates for VEX statements affecting the tenant: - Event types: `statement.created`, `statement.updated`, `statement.deleted`, `statement.conflict`. - Fields: `id`, `advisoryId`, `product`, `vexState`, `severityHint`, `policyBadge`, `conflictSummary`, `sequence`. - Replay: Clients include `Last-Event-ID`; server resumes from sequence. - Heartbeats every 15 seconds (`event: keepalive`, `data: {}`). Example event payload: ```jsonc event: statement.updated data: { "statementId": "vex:tenant-default:jwt-auth:5d1a", "advisoryId": "CVE-2024-12345", "product": "registry.local/ops/auth:2025.10.0", "state": "fixed", "justification": "solution_available", "sequence": 4182, "updatedAt": "2025-11-08T11:44:32Z" } ``` ## 5. Signals & Scheduler Integration - Reachability data is materialized by Scheduler delta jobs (`SCHED-CONSOLE-23-001`). `/console/vuln/findings` should cache the most recent job ID and expose `signalsVersion`. - VEX justification fields reference Excititor statement IDs; ensure the gateway checks Excititor availability and degrades gracefully (returns `state: unavailable` plus telemetry). - Scheduler must publish `console.vuln.refresh` events whenever advisory/VEX deltas warrant workspace refresh; console SSE endpoint may piggyback on the same Redis/NATS channel. ## 6. Determinism & Offline Notes 1. All responses are compressible JSON; no CDN fonts/assets referenced. 2. SSE endpoints must tolerate sealed mode by operating on loopback addresses only. 3. `authority-sealed-ci.json` (see DEVOPS-AIRGAP-57-002) is the evidence Authority consumes before enabling these APIs for sealed tenants; configure `airGap.sealedMode` and set `requiresAirgapSealConfirmation: true` on the Console client so `/token` refuses requests until the sealed harness uploads a fresh, passing artefact. Console responses echo `sealed: true/false` flags for UI badges once Authority has confirmed the evidence. ## 7. Sample Payloads for Docs - `docs/api/console/samples/vuln-findings-sample.json` – exported via `scripts/generate-console-samples.ts` (placeholder script to be added when backend lands). - `docs/api/console/samples/vex-statement-sse.ndjson` – contains 5 chronological SSE events for screenshot reproduction. > Until backend implementations ship, use the examples above to unblock DOCS-AIAI-31-004; replace them with live captures once the gateway endpoints are available in staging. ## Exports (draft contract) Routes - `POST /console/exports` — start an evidence bundle export job. - `GET /console/exports/{exportId}` — fetch job status and download locations. - `GET /console/exports/{exportId}/events` — SSE stream of job progress (optional). Headers - `Authorization: Bearer ` - `X-StellaOps-Tenant: ` - `Idempotency-Key: ` (recommended for POST) - `Accept: application/json` (status) or `text/event-stream` (events) Request body (POST /console/exports) - `scope`: `{ tenantId, projectId? }` - `sources`: array of `{ type: "advisory"|"vex"|"policy"|"scan", ids: string[] }` - `formats`: array of `"json"|"csv"|"ndjson"|"pdf"` - `attestations`: `{ include: boolean, sigstoreBundle?: boolean }` - `notify`: `{ webhooks?: string[], email?: string[] }` - `priority`: `"low"|"normal"|"high"` Responses - `202 Accepted` with `exportId`, `status: queued|running|succeeded|failed|expired`, `estimateSeconds`, `retryAfter`. - Status payload includes presigned download URLs, checksum manifest, and error list when failed. - SSE events emit `started`, `progress` (percent, item counts), `asset_ready` (uri, sha256), `completed`, `failed` (code, message). Proposed limits - Max request body 256 KiB; max sources 50; max outputs 1000 assets/export. - Default job timeout 30 minutes; idle SSE timeout 60s; backoff header `Retry-After`. Samples (draft) - Request: `docs/api/console/samples/console-export-request.json` - Status: `docs/api/console/samples/console-export-status.json` - Manifest: `docs/api/console/samples/console-export-manifest.json` - Events: `docs/api/console/samples/console-export-events.ndjson` Open items (needs owner sign-off) - Final schema (fields, limits, error codes), checksum manifest format, attestation options. - Caching/tie-break rules for downstream `/console/search` and `/console/downloads`.