Files
git.stella-ops.org/docs/export-center/api.md
root 68da90a11a
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Restructure solution layout by module
2025-10-28 15:10:40 +02:00

11 KiB

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 and Profiles 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:

{
  "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

{
  "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

{
  "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

{
  "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

{
  "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

{
  "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.
  • 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.

Imposed rule: Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.