feat(docs): Add comprehensive documentation for Vexer, Vulnerability Explorer, and Zastava modules
- Introduced AGENTS.md, README.md, TASKS.md, and implementation_plan.md for Vexer, detailing mission, responsibilities, key components, and operational notes. - Established similar documentation structure for Vulnerability Explorer and Zastava modules, including their respective workflows, integrations, and observability notes. - Created risk scoring profiles documentation outlining the core workflow, factor model, governance, and deliverables. - Ensured all modules adhere to the Aggregation-Only Contract and maintain determinism and provenance in outputs.
This commit is contained in:
		
							
								
								
									
										337
									
								
								docs/modules/export-center/api.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								docs/modules/export-center/api.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,337 @@ | ||||
| # Export Center REST API | ||||
|  | ||||
| > **Audience:** Platform integrators, Console/CLI developers, and automation engineers orchestrating export runs.   | ||||
| > **Base route:** `/api/export/*` behind the StellaOps gateway; requires Authority-issued tokens with export scopes. | ||||
|  | ||||
| This reference describes the Export Center API introduced in Export Center Phase 1 (Epic 10) and extended in Phase 2. Use it alongside the [Export Center Architecture](architecture.md) and [Profiles](profiles.md) guides for service-level semantics. | ||||
|  | ||||
| > Status: Endpoint implementation lands with `EXPORT-SVC-35-006` (Sprint 35) and related follow-on tasks. As of the current build the WebService hosts only the template stub; use this contract for coordination and update once the API is wired. | ||||
|  | ||||
| ## 1. Authentication and headers | ||||
|  | ||||
| - **Authorization:** Bearer tokens in `Authorization: Bearer <token>` paired with DPoP proof. Required scopes per endpoint: | ||||
|   - `export:profile:manage` for profile CRUD. | ||||
|   - `export:run` to submit and cancel runs. | ||||
|   - `export:read` to list and inspect runs. | ||||
|   - `export:download` for bundle downloads and manifests. | ||||
| - **Tenant context:** Provide `X-Stella-Tenant` when the token carries multiple tenants; defaults to token tenant otherwise. | ||||
| - **Idempotency:** Mutating endpoints accept `Idempotency-Key` (UUID). Retrying with the same key returns the original result. | ||||
| - **Rate limits and quotas:** Responses include `X-Stella-Quota-Limit`, `X-Stella-Quota-Remaining`, and `X-Stella-Quota-Reset`. Exceeding quotas returns `429 Too Many Requests` with `ERR_EXPORT_QUOTA`. | ||||
| - **Content negotiation:** Requests and responses use `application/json; charset=utf-8` unless otherwise stated. Downloads stream binary content with profile-specific media types. | ||||
| - **SSE:** Event streams set `Content-Type: text/event-stream` and keep connections alive with comment heartbeats every 15 seconds. | ||||
|  | ||||
| ## 2. Error model | ||||
|  | ||||
| Errors follow standard HTTP codes with structured payloads: | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "code": "ERR_EXPORT_002", | ||||
|   "message": "Profile not found for tenant acme", | ||||
|   "details": [], | ||||
|   "traceId": "01J9N4Y4K2XY8C5V7T2S", | ||||
|   "timestamp": "2025-10-29T13:42:11Z" | ||||
| } | ||||
| ``` | ||||
|  | ||||
| | Code | Description | Typical HTTP status | Notes | | ||||
| |------|-------------|---------------------|-------| | ||||
| | `ERR_EXPORT_001` | Validation failure (selectors, configuration) | 400 | `details` enumerates offending fields. | | ||||
| | `ERR_EXPORT_002` | Profile missing or not accessible for tenant | 404 | Returned on run submission or profile fetch. | | ||||
| | `ERR_EXPORT_003` | Concurrency or quota exceeded | 429 | Includes `retryAfterSeconds` in `details`. | | ||||
| | `ERR_EXPORT_004` | Adapter failure (schema mismatch, upstream outage) | 502 | Worker logs contain adapter error reason. | | ||||
| | `ERR_EXPORT_005` | Signing or KMS error | 500 | Run marked failed with `errorCode=signing`. | | ||||
| | `ERR_EXPORT_006` | Distribution failure (HTTP, OCI, object storage) | 502 | `details` lists failing distribution driver. | | ||||
| | `ERR_EXPORT_007` | Run canceled or expired | 409 | Includes cancel author and timestamp. | | ||||
| | `ERR_EXPORT_BASE_MISSING` | Base manifest for delta exports not found | 400 | Specific to `mirror:delta`. | | ||||
| | `ERR_EXPORT_EMPTY` | No records matched selectors (when `allowEmpty=false`) | 422 | Useful for guard-railled automation. | | ||||
| | `ERR_EXPORT_QUOTA` | Daily quota exhausted | 429 | Always paired with quota headers. | | ||||
|  | ||||
| All responses include `traceId` for correlation with logs and metrics. | ||||
|  | ||||
| ## 3. Profiles endpoints | ||||
|  | ||||
| ### 3.1 List profiles | ||||
|  | ||||
| ``` | ||||
| GET /api/export/profiles?kind=json&variant=raw&page=1&pageSize=20 | ||||
| Scopes: export:read | ||||
| ``` | ||||
|  | ||||
| Returns tenant-scoped profiles. Response headers: `X-Total-Count`, `Link` for pagination. | ||||
|  | ||||
| **Response** | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "items": [ | ||||
|     { | ||||
|       "profileId": "prof-json-raw", | ||||
|       "name": "Daily JSON Raw", | ||||
|       "kind": "json", | ||||
|       "variant": "raw", | ||||
|       "distribution": ["http", "object"], | ||||
|       "retention": {"mode": "days", "value": 14}, | ||||
|       "createdAt": "2025-10-23T08:00:00Z", | ||||
|       "createdBy": "user:ops" | ||||
|     } | ||||
|   ], | ||||
|   "page": 1, | ||||
|   "pageSize": 20 | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### 3.2 Get a profile | ||||
|  | ||||
| ``` | ||||
| GET /api/export/profiles/{profileId} | ||||
| Scopes: export:read | ||||
| ``` | ||||
|  | ||||
| Returns full configuration, including `config` payload, distribution options, and metadata. | ||||
|  | ||||
| ### 3.3 Create a profile | ||||
|  | ||||
| ``` | ||||
| POST /api/export/profiles | ||||
| Scopes: export:profile:manage | ||||
| ``` | ||||
|  | ||||
| **Request** | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "profileId": "prof-airgap-mirror", | ||||
|   "name": "Airgap Mirror Weekly", | ||||
|   "kind": "mirror", | ||||
|   "variant": "full", | ||||
|   "include": ["advisories", "vex", "sboms", "policy"], | ||||
|   "distribution": ["http", "object"], | ||||
|   "encryption": { | ||||
|     "enabled": true, | ||||
|     "recipientKeys": ["age1tenantkey..."], | ||||
|     "strict": false | ||||
|   }, | ||||
|   "retention": {"mode": "days", "value": 30} | ||||
| } | ||||
| ``` | ||||
|  | ||||
| **Response 201** | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "profileId": "prof-airgap-mirror", | ||||
|   "version": 1, | ||||
|   "createdAt": "2025-10-29T12:05:22Z", | ||||
|   "createdBy": "user:ops", | ||||
|   "status": "active" | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### 3.4 Update profile metadata | ||||
|  | ||||
| ``` | ||||
| PATCH /api/export/profiles/{profileId} | ||||
| Scopes: export:profile:manage | ||||
| ``` | ||||
|  | ||||
| Allows renaming, toggling distribution switches, or updating retention. Structural configuration updates (kind/variant/include) create a new revision; the API returns `revisionCreated=true` and the new `profileId` (e.g., `prof-airgap-mirror@2`). | ||||
|  | ||||
| ### 3.5 Archive profile | ||||
|  | ||||
| ``` | ||||
| POST /api/export/profiles/{profileId}:archive | ||||
| Scopes: export:profile:manage | ||||
| ``` | ||||
|  | ||||
| Marks profile as inactive; existing runs remain accessible. Use `:restore` to reactivate. | ||||
|  | ||||
| ## 4. Run management | ||||
|  | ||||
| ### 4.1 Submit an export run | ||||
|  | ||||
| ``` | ||||
| POST /api/export/runs | ||||
| Scopes: export:run | ||||
| ``` | ||||
|  | ||||
| **Request** | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "profileId": "prof-json-raw", | ||||
|   "selectors": { | ||||
|     "tenants": ["acme"], | ||||
|     "timeWindow": { | ||||
|       "from": "2025-10-01T00:00:00Z", | ||||
|       "to": "2025-10-29T00:00:00Z" | ||||
|     }, | ||||
|     "products": ["registry.example.com/app:*"], | ||||
|     "sboms": ["sbom:S-1001", "sbom:S-2004"] | ||||
|   }, | ||||
|   "policySnapshotId": "policy-snap-42", | ||||
|   "options": { | ||||
|     "allowEmpty": false, | ||||
|     "priority": "standard" | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| **Response 202** | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "runId": "run-20251029-01", | ||||
|   "status": "pending", | ||||
|   "profileId": "prof-json-raw", | ||||
|   "createdAt": "2025-10-29T12:12:11Z", | ||||
|   "createdBy": "user:ops", | ||||
|   "selectors": { "...": "..." }, | ||||
|   "links": { | ||||
|     "self": "/api/export/runs/run-20251029-01", | ||||
|     "events": "/api/export/runs/run-20251029-01/events" | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### 4.2 List runs | ||||
|  | ||||
| ``` | ||||
| GET /api/export/runs?status=active&profileId=prof-json-raw&page=1&pageSize=10 | ||||
| Scopes: export:read | ||||
| ``` | ||||
|  | ||||
| Returns latest runs with pagination. Each item includes summary counts, duration, and last event. | ||||
|  | ||||
| ### 4.3 Get run status | ||||
|  | ||||
| ``` | ||||
| GET /api/export/runs/{runId} | ||||
| Scopes: export:read | ||||
| ``` | ||||
|  | ||||
| Response fields: | ||||
|  | ||||
| | Field | Description | | ||||
| |-------|-------------| | ||||
| | `status` | `pending`, `running`, `success`, `failed`, `canceled`. | | ||||
| | `progress` | Object with `adapters`, `bytesWritten`, `recordsProcessed`. | | ||||
| | `errorCode` | Populated when `status=failed` (`signing`, `distribution`, etc). | | ||||
| | `policySnapshotId` | Returned for policy-aware profiles. | | ||||
| | `distributions` | List of available distribution descriptors (type, location, sha256, expiresAt). | | ||||
|  | ||||
| ### 4.4 Cancel a run | ||||
|  | ||||
| ``` | ||||
| POST /api/export/runs/{runId}:cancel | ||||
| Scopes: export:run | ||||
| ``` | ||||
|  | ||||
| Body optional (`{"reason": "Aborted due to incident INC-123"}`). Returns 202 and pushes `run.canceled` event. | ||||
|  | ||||
| ## 5. Events and telemetry | ||||
|  | ||||
| ### 5.1 Server-sent events | ||||
|  | ||||
| ``` | ||||
| GET /api/export/runs/{runId}/events | ||||
| Scopes: export:read | ||||
| Accept: text/event-stream | ||||
| ``` | ||||
|  | ||||
| Event payload example: | ||||
|  | ||||
| ``` | ||||
| event: run.progress | ||||
| data: {"runId":"run-20251029-01","phase":"adapter","adapter":"json","records":1024,"bytes":7340032,"timestamp":"2025-10-29T12:13:15Z"} | ||||
| ``` | ||||
|  | ||||
| Event types: | ||||
|  | ||||
| | Event | Meaning | | ||||
| |-------|---------| | ||||
| | `run.accepted` | Planner accepted job and queued with Orchestrator. | | ||||
| | `run.progress` | Periodic updates with phase, adapter, counts. | | ||||
| | `run.distribution` | Distribution driver finished (includes descriptor). | | ||||
| | `run.signed` | Signing completed successfully. | | ||||
| | `run.succeeded` | Run marked `success`. | | ||||
| | `run.failed` | Run failed; payload includes `errorCode`. | | ||||
| | `run.canceled` | Run canceled; includes `canceledBy`. | | ||||
|  | ||||
| SSE heartbeats (`: ping`) keep long-lived connections alive and should be ignored by clients. | ||||
|  | ||||
| ### 5.2 Audit events | ||||
|  | ||||
| `GET /api/export/runs/{runId}/events?format=audit` returns the same event stream in newline-delimited JSON for offline ingestion. | ||||
|  | ||||
| ## 6. Download endpoints | ||||
|  | ||||
| ### 6.1 Bundle download | ||||
|  | ||||
| ``` | ||||
| GET /api/export/runs/{runId}/download | ||||
| Scopes: export:download | ||||
| ``` | ||||
|  | ||||
| Streams the primary bundle (tarball, zip, or profile-specific layout). Headers: | ||||
|  | ||||
| - `Content-Disposition: attachment; filename="export-run-20251029-01.tar.zst"` | ||||
| - `X-Export-Digest: sha256:...` | ||||
| - `X-Export-Size: 73482019` | ||||
| - `X-Export-Encryption: age` (when mirror encryption enabled) | ||||
|  | ||||
| Supports HTTP range requests for resume functionality. If no bundle exists yet, responds `409` with `ERR_EXPORT_007`. | ||||
|  | ||||
| ### 6.2 Manifest download | ||||
|  | ||||
| ``` | ||||
| GET /api/export/runs/{runId}/manifest | ||||
| Scopes: export:download | ||||
| ``` | ||||
|  | ||||
| Returns signed `export.json`. To fetch the detached signature, append `?signature=true`. | ||||
|  | ||||
| ### 6.3 Provenance download | ||||
|  | ||||
| ``` | ||||
| GET /api/export/runs/{runId}/provenance | ||||
| Scopes: export:download | ||||
| ``` | ||||
|  | ||||
| Returns signed `provenance.json`. Supports `?signature=true`. Provenance includes attestation subject digests, policy snapshot ids, adapter versions, and KMS key identifiers. | ||||
|  | ||||
| ### 6.4 Distribution descriptors | ||||
|  | ||||
| ``` | ||||
| GET /api/export/runs/{runId}/distributions | ||||
| Scopes: export:read | ||||
| ``` | ||||
|  | ||||
| Lists all registered distribution targets (HTTP, OCI, object storage). Each item includes `type`, `location`, `sha256`, `sizeBytes`, and `expiresAt`. | ||||
|  | ||||
| ## 7. Webhook hand-off | ||||
|  | ||||
| Exports can notify external systems once a run succeeds by registering an HTTP webhook: | ||||
|  | ||||
| ``` | ||||
| POST /api/export/webhooks | ||||
| Scopes: export:profile:manage | ||||
| ``` | ||||
|  | ||||
| Payload includes `targetUrl`, `events` (e.g., `run.succeeded`), and optional secret for HMAC signatures. Webhook deliveries sign payloads with `X-Stella-Signature` header (`sha256=...`). Retries follow exponential backoff with dead-letter capture in `export_events`. | ||||
|  | ||||
| ## 8. Observability | ||||
|  | ||||
| - **Metrics endpoint:** `/metrics` (service-local) exposes Prometheus metrics listed in [Architecture](architecture.md#observability). | ||||
| - **Tracing:** When `traceparent` header is provided, worker spans join the calling trace. | ||||
| - **Run lookup by trace:** Use `GET /api/export/runs?traceId={id}` when troubleshooting distributed traces. | ||||
|  | ||||
| ## 9. Related documentation | ||||
|  | ||||
| - [Export Center Overview](overview.md) | ||||
| - [Export Center Architecture](architecture.md) | ||||
| - [Export Center Profiles](profiles.md) | ||||
| - [Export Center CLI Guide](cli.md) *(companion document)* | ||||
| - [Aggregation-Only Contract reference](../../ingestion/aggregation-only-contract.md) | ||||
|  | ||||
| > **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied. | ||||
		Reference in New Issue
	
	Block a user